diff --git a/README.md b/README.md index a2cea56..dde3485 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,22 @@ +# MedReport README -## Prerequisites - "node": ">=20.0.0", - "pnpm": ">=9.0.0" +## Prerequisites + +```json +"node": ">=20.0.0", +"pnpm": ">=9.0.0" +``` ## Project structure -``` + +```text / app - pages / components - custom components, helper components that not provided by any package. Place to extend an redefine components from packages -/ config - bunch of configs, that are provided by starter kit. +/ config - bunch of configs, that are provided by starter kit. / content - (temporary?) - to be removed when cleaned all dependencies / fonts - (temporary) - contains fonts, should be relocated to another place (maybe public) / lib - diffirent libs, services, utils - - fonts.ts - project fonts setup, which becomes available as a global css variable + - fonts.ts - project fonts setup, which becomes available as a global css variable / i18n - translations/localization setup / public - public assets / locales - translations under a corresponding local - at a specific namespace @@ -21,26 +26,47 @@ - theme.css - more specific variables, available as tailwindcss property-class - makerkit.css - Makerkit-specific global styles - markdoc.css - Styles for Markdoc Markdown files. - - + - / supabase - primary supabase / tooling - a workspace package, used for generation packages in node_modules and provides global links for its data. The most important is typescript config / utils - - ``` - ## Migration from old structure -```bash + +```bash pnpm clean pnpm i ``` ## Adding new dependency -```bash +```bash pnpm add -w ``` ## Supabase -TODO \ No newline at end of file + +Start supabase in docker + +```bash +npm run supabase:start +``` + +Link your local supabase with a supabase project + +```bash +npm run supabase:deploy +``` + +After editing supabase tables/functions etc update migration files + +```bash +npm run supabase:db:diff +``` + +To update database types run: + +```bash +npm run supabase:typegen:app +``` diff --git a/app/(marketing)/page.tsx b/app/(marketing)/page.tsx index 09dd6bc..1726d34 100644 --- a/app/(marketing)/page.tsx +++ b/app/(marketing)/page.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; -import { MedReportLogo } from '@/components/med-report-title'; +import { MedReportLogo } from '@/components/med-report-logo'; import { ArrowRightIcon } from 'lucide-react'; import { CtaButton, Hero } from '@kit/ui/marketing'; @@ -49,7 +49,7 @@ function MainCallToActionButton() { - + diff --git a/app/(public)/register-company/page.tsx b/app/(public)/company-offer/page.tsx similarity index 82% rename from app/(public)/register-company/page.tsx rename to app/(public)/company-offer/page.tsx index 881a0fc..bf68684 100644 --- a/app/(public)/register-company/page.tsx +++ b/app/(public)/company-offer/page.tsx @@ -4,12 +4,11 @@ import React from 'react'; import { useRouter } from 'next/navigation'; -import { MedReportLogo } from '@/components/med-report-title'; +import { MedReportLogo } from '@/components/med-report-logo'; import { SubmitButton } from '@/components/ui/submit-button'; import { sendCompanyOfferEmail } from '@/lib/services/mailer.service'; -import { submitCompanyRegistration } from '@/lib/services/register-company.service'; import { CompanySubmitData } from '@/lib/types/company'; -import { companySchema } from '@/lib/validations/companySchema'; +import { companyOfferSchema } from '@/lib/validations/company-offer.schema'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -19,36 +18,35 @@ import { Input } from '@kit/ui/input'; import { Label } from '@kit/ui/label'; import { Trans } from '@kit/ui/trans'; -export default function RegisterCompany() { +export default function CompanyOffer() { const router = useRouter(); const { register, handleSubmit, - formState: { errors, isValid, isSubmitting }, + formState: { isValid, isSubmitting }, } = useForm({ - resolver: zodResolver(companySchema), + resolver: zodResolver(companyOfferSchema), mode: 'onChange', }); const language = useTranslation().i18n.language; - async function onSubmit(data: CompanySubmitData) { + const onSubmit = async (data: CompanySubmitData) => { const formData = new FormData(); Object.entries(data).forEach(([key, value]) => { if (value !== undefined) formData.append(key, value); }); try { - await submitCompanyRegistration(formData); sendCompanyOfferEmail(data, language) - .then(() => router.push('/register-company/success')) + .then(() => router.push('/company-offer/success')) .catch((error) => alert('error: ' + error)); } catch (err: unknown) { if (err instanceof Error) { - alert('Server validation error: ' + err.message); + console.warn('Server validation error: ' + err.message); } - alert('Server validation error'); + console.warn('Server validation error: ', err); } - } + }; return (
diff --git a/app/(public)/company-offer/success/page.tsx b/app/(public)/company-offer/success/page.tsx new file mode 100644 index 0000000..6c136c7 --- /dev/null +++ b/app/(public)/company-offer/success/page.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { SuccessNotification } from '@/packages/features/notifications/src/components'; + +export default function CompanyRegistrationSuccess() { + return ( + + ); +} diff --git a/app/(public)/register-company/success/page.tsx b/app/(public)/register-company/success/page.tsx deleted file mode 100644 index 42f2634..0000000 --- a/app/(public)/register-company/success/page.tsx +++ /dev/null @@ -1,37 +0,0 @@ -'use client'; - -import Image from 'next/image'; -import Link from 'next/link'; - -import { MedReportLogo } from '@/components/med-report-title'; - -import { Button } from '@kit/ui/button'; -import { Trans } from '@kit/ui/trans'; - -export default function CompanyRegistrationSuccess() { - return ( -
- -
- Success -

- -

-

- -

-
- -
- ); -} diff --git a/app/(public)/sign-in/page.tsx b/app/(public)/sign-in/page.tsx index aca9ead..3d64786 100644 --- a/app/(public)/sign-in/page.tsx +++ b/app/(public)/sign-in/page.tsx @@ -1,6 +1,8 @@ +import React from 'react'; + +import Link from 'next/link'; + import { Button } from '@kit/ui/button'; -import Link from "next/link"; -import React from "react"; export default async function SignIn() { return ( @@ -15,7 +17,7 @@ export default async function SignIn() { ID-Kaart
); diff --git a/app/auth/layout.tsx b/app/auth/callback/layout.tsx similarity index 100% rename from app/auth/layout.tsx rename to app/auth/callback/layout.tsx diff --git a/app/auth/confirm/layout.tsx b/app/auth/confirm/layout.tsx new file mode 100644 index 0000000..ff7b33b --- /dev/null +++ b/app/auth/confirm/layout.tsx @@ -0,0 +1,9 @@ +import { AuthLayoutShell } from '@kit/auth/shared'; + +import { AppLogo } from '~/components/app-logo'; + +function AuthLayout({ children }: React.PropsWithChildren) { + return {children}; +} + +export default AuthLayout; diff --git a/app/auth/password-reset/layout.tsx b/app/auth/password-reset/layout.tsx new file mode 100644 index 0000000..ff7b33b --- /dev/null +++ b/app/auth/password-reset/layout.tsx @@ -0,0 +1,9 @@ +import { AuthLayoutShell } from '@kit/auth/shared'; + +import { AppLogo } from '~/components/app-logo'; + +function AuthLayout({ children }: React.PropsWithChildren) { + return {children}; +} + +export default AuthLayout; diff --git a/app/auth/sign-in/layout.tsx b/app/auth/sign-in/layout.tsx new file mode 100644 index 0000000..ff7b33b --- /dev/null +++ b/app/auth/sign-in/layout.tsx @@ -0,0 +1,9 @@ +import { AuthLayoutShell } from '@kit/auth/shared'; + +import { AppLogo } from '~/components/app-logo'; + +function AuthLayout({ children }: React.PropsWithChildren) { + return {children}; +} + +export default AuthLayout; diff --git a/app/auth/sign-in/page.tsx b/app/auth/sign-in/page.tsx index 16ad40c..4fc9223 100644 --- a/app/auth/sign-in/page.tsx +++ b/app/auth/sign-in/page.tsx @@ -1,5 +1,7 @@ import Link from 'next/link'; +import { register } from 'module'; + import { SignInMethodsContainer } from '@kit/auth/sign-in'; import { Button } from '@kit/ui/button'; import { Heading } from '@kit/ui/heading'; @@ -26,7 +28,8 @@ export const generateMetadata = async () => { }; async function SignInPage({ searchParams }: SignInPageProps) { - const { invite_token: inviteToken, next = '' } = await searchParams; + const { invite_token: inviteToken, next = pathsConfig.app.home } = + await searchParams; const signUpPath = pathsConfig.auth.signUp + @@ -36,6 +39,7 @@ async function SignInPage({ searchParams }: SignInPageProps) { callback: pathsConfig.auth.callback, returnPath: next ?? pathsConfig.app.home, joinTeam: pathsConfig.app.joinTeam, + updateAccount: pathsConfig.auth.updateAccount, }; return ( diff --git a/app/auth/sign-up/layout.tsx b/app/auth/sign-up/layout.tsx new file mode 100644 index 0000000..ff7b33b --- /dev/null +++ b/app/auth/sign-up/layout.tsx @@ -0,0 +1,9 @@ +import { AuthLayoutShell } from '@kit/auth/shared'; + +import { AppLogo } from '~/components/app-logo'; + +function AuthLayout({ children }: React.PropsWithChildren) { + return {children}; +} + +export default AuthLayout; diff --git a/app/auth/sign-up/page.tsx b/app/auth/sign-up/page.tsx index 40b1eab..61acc81 100644 --- a/app/auth/sign-up/page.tsx +++ b/app/auth/sign-up/page.tsx @@ -27,6 +27,7 @@ interface Props { const paths = { callback: pathsConfig.auth.callback, appHome: pathsConfig.app.home, + updateAccount: pathsConfig.auth.updateAccount, }; async function SignUpPage({ searchParams }: Props) { diff --git a/app/auth/update-account/layout.tsx b/app/auth/update-account/layout.tsx new file mode 100644 index 0000000..8212f3c --- /dev/null +++ b/app/auth/update-account/layout.tsx @@ -0,0 +1,11 @@ +import { withI18n } from '~/lib/i18n/with-i18n'; + +async function SiteLayout(props: React.PropsWithChildren) { + return ( +
+ {props.children} +
+ ); +} + +export default withI18n(SiteLayout); diff --git a/app/auth/update-account/page.tsx b/app/auth/update-account/page.tsx new file mode 100644 index 0000000..8d0231c --- /dev/null +++ b/app/auth/update-account/page.tsx @@ -0,0 +1,43 @@ +import { redirect } from 'next/navigation'; + +import { BackButton } from '@/components/back-button'; +import { MedReportLogo } from '@/components/med-report-logo'; +import pathsConfig from '@/config/paths.config'; +import { signOutAction } from '@/lib/actions/sign-out'; +import { UpdateAccountForm } from '@/packages/features/auth/src/components/update-account-form'; +import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; + +import { Trans } from '@kit/ui/trans'; + +import { withI18n } from '~/lib/i18n/with-i18n'; + +async function UpdateAccount() { + const client = getSupabaseServerClient(); + + const { + data: { user }, + } = await client.auth.getUser(); + + if (!user) { + redirect(pathsConfig.auth.signIn); + } + + return ( +
+
+ + +

+ +

+

+ +

+ +
+
+
+ ); +} + +export default withI18n(UpdateAccount); diff --git a/app/auth/update-account/success/page.tsx b/app/auth/update-account/success/page.tsx new file mode 100644 index 0000000..a31b5d2 --- /dev/null +++ b/app/auth/update-account/success/page.tsx @@ -0,0 +1,17 @@ +import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; + +import { UpdateAccountSuccessNotification } from '@kit/notifications/components'; + +import { withI18n } from '~/lib/i18n/with-i18n'; + +async function UpdateAccountSuccess() { + const client = getSupabaseServerClient(); + + const { + data: { user }, + } = await client.auth.getUser(); + + return ; +} + +export default withI18n(UpdateAccountSuccess); diff --git a/app/auth/verify/layout.tsx b/app/auth/verify/layout.tsx new file mode 100644 index 0000000..ff7b33b --- /dev/null +++ b/app/auth/verify/layout.tsx @@ -0,0 +1,9 @@ +import { AuthLayoutShell } from '@kit/auth/shared'; + +import { AppLogo } from '~/components/app-logo'; + +function AuthLayout({ children }: React.PropsWithChildren) { + return {children}; +} + +export default AuthLayout; diff --git a/app/home/(user)/billing/_lib/server/user-billing.service.ts b/app/home/(user)/billing/_lib/server/user-billing.service.ts index 0fba68f..41506e8 100644 --- a/app/home/(user)/billing/_lib/server/user-billing.service.ts +++ b/app/home/(user)/billing/_lib/server/user-billing.service.ts @@ -2,6 +2,7 @@ import 'server-only'; import { SupabaseClient } from '@supabase/supabase-js'; +import { Database } from '@/packages/supabase/src/database.types'; import { z } from 'zod'; import { createAccountsApi } from '@kit/accounts/api'; @@ -13,7 +14,6 @@ import { requireUser } from '@kit/supabase/require-user'; import appConfig from '~/config/app.config'; import billingConfig from '~/config/billing.config'; import pathsConfig from '~/config/paths.config'; -import { Database } from '~/lib/database.types'; import { PersonalAccountCheckoutSchema } from '../schema/personal-account-checkout.schema'; diff --git a/app/home/[account]/billing/_lib/server/team-billing.service.ts b/app/home/[account]/billing/_lib/server/team-billing.service.ts index 93cb17e..b2c285d 100644 --- a/app/home/[account]/billing/_lib/server/team-billing.service.ts +++ b/app/home/[account]/billing/_lib/server/team-billing.service.ts @@ -2,6 +2,7 @@ import 'server-only'; import { SupabaseClient } from '@supabase/supabase-js'; +import { Database } from '@/packages/supabase/src/database.types'; import { z } from 'zod'; import { LineItemSchema } from '@kit/billing'; @@ -14,7 +15,6 @@ import { createTeamAccountsApi } from '@kit/team-accounts/api'; import appConfig from '~/config/app.config'; import billingConfig from '~/config/billing.config'; import pathsConfig from '~/config/paths.config'; -import { Database } from '~/lib/database.types'; import { TeamCheckoutSchema } from '../schema/team-billing.schema'; diff --git a/app/home/[account]/members/_lib/server/members-page.loader.ts b/app/home/[account]/members/_lib/server/members-page.loader.ts index 4db49b5..e77fd3f 100644 --- a/app/home/[account]/members/_lib/server/members-page.loader.ts +++ b/app/home/[account]/members/_lib/server/members-page.loader.ts @@ -2,8 +2,9 @@ import 'server-only'; import { SupabaseClient } from '@supabase/supabase-js'; +import { Database } from '@/packages/supabase/src/database.types'; + import { loadTeamWorkspace } from '~/home/[account]/_lib/server/team-account-workspace.loader'; -import { Database } from '~/lib/database.types'; /** * Load data for the members page diff --git a/components/app-logo.tsx b/components/app-logo.tsx index d11f922..82a1179 100644 --- a/components/app-logo.tsx +++ b/components/app-logo.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; -import { MedReportLogo } from './med-report-title'; +import { MedReportLogo } from './med-report-logo'; function LogoImage({ className, diff --git a/components/back-button.tsx b/components/back-button.tsx new file mode 100644 index 0000000..0e01177 --- /dev/null +++ b/components/back-button.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { useRouter } from 'next/navigation'; + +import { ArrowLeft } from '@/public/assets/arrow-left'; + +import { Trans } from '@kit/ui/trans'; + +export function BackButton({ onBack }: { onBack?: () => void }) { + const router = useRouter(); + return ( +
{ + if (onBack) { + onBack(); + } else { + router.back(); + } + }} + > + +
+ ); +} diff --git a/components/header-auth.tsx b/components/header-auth.tsx index d560200..0fd2179 100644 --- a/components/header-auth.tsx +++ b/components/header-auth.tsx @@ -1,9 +1,11 @@ -import { signOutAction } from "@/lib/actions/sign-out"; -import { hasEnvVars } from "@/utils/supabase/check-env-vars"; -import Link from "next/link"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { createClient } from "@/utils/supabase/server"; +import Link from 'next/link'; + +import { signOutAction } from '@/lib/actions/sign-out'; +import { hasEnvVars } from '@/utils/supabase/check-env-vars'; +import { createClient } from '@/utils/supabase/server'; + +import { Badge } from '@kit/ui/badge'; +import { Button } from '@kit/ui/button'; export default async function AuthButton() { const supabase = await createClient(); @@ -15,11 +17,11 @@ export default async function AuthButton() { if (!hasEnvVars) { return ( <> -
+
Please update .env.local file with anon key and url @@ -28,18 +30,18 @@ export default async function AuthButton() { @@ -52,14 +54,14 @@ export default async function AuthButton() {
Hey, {user.email}!
-
) : (
-
diff --git a/components/med-report-title.tsx b/components/med-report-logo.tsx similarity index 100% rename from components/med-report-title.tsx rename to components/med-report-logo.tsx diff --git a/config/paths.config.ts b/config/paths.config.ts index ad0bf12..340e208 100644 --- a/config/paths.config.ts +++ b/config/paths.config.ts @@ -8,6 +8,8 @@ const PathsSchema = z.object({ callback: z.string().min(1), passwordReset: z.string().min(1), passwordUpdate: z.string().min(1), + updateAccount: z.string().min(1), + updateAccountSuccess: z.string().min(1), }), app: z.object({ home: z.string().min(1), @@ -31,6 +33,8 @@ const pathsConfig = PathsSchema.parse({ callback: '/auth/callback', passwordReset: '/auth/password-reset', passwordUpdate: '/update-password', + updateAccount: '/auth/update-account', + updateAccountSuccess: '/auth/update-account/success', }, app: { home: '/home', diff --git a/lib/actions/sign-out.tsx b/lib/actions/sign-out.tsx index 0137a93..a168684 100644 --- a/lib/actions/sign-out.tsx +++ b/lib/actions/sign-out.tsx @@ -1,10 +1,11 @@ -"use server"; +'use server'; -import { createClient } from "@/utils/supabase/server"; -import { redirect } from "next/navigation"; +import { redirect } from 'next/navigation'; + +import { createClient } from '@/utils/supabase/server'; export const signOutAction = async () => { const supabase = await createClient(); await supabase.auth.signOut(); - return redirect("/sign-in"); + return redirect('/'); }; diff --git a/lib/services/register-company.service.ts b/lib/services/register-company.service.ts deleted file mode 100644 index 7b973bd..0000000 --- a/lib/services/register-company.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -'use server'; - -import { companySchema } from '@/lib/validations/companySchema'; - -export async function submitCompanyRegistration(formData: FormData) { - const data = { - companyName: formData.get('companyName')?.toString() || '', - contactPerson: formData.get('contactPerson')?.toString() || '', - email: formData.get('email')?.toString() || '', - phone: formData.get('phone')?.toString() || '', - }; - - const result = companySchema.safeParse(data); - - if (!result.success) { - const errors = result.error.errors.map((err) => ({ - path: err.path.join('.'), - message: err.message, - })); - - throw new Error( - 'Validation failed: ' + - errors.map((e) => `${e.path}: ${e.message}`).join(', '), - ); - } -} diff --git a/lib/validations/companySchema.ts b/lib/validations/company-offer.schema.ts similarity index 88% rename from lib/validations/companySchema.ts rename to lib/validations/company-offer.schema.ts index aa35ebc..4ac5b5d 100644 --- a/lib/validations/companySchema.ts +++ b/lib/validations/company-offer.schema.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -export const companySchema = z.object({ +export const companyOfferSchema = z.object({ companyName: z.string({ required_error: 'Company name is required', }), diff --git a/middleware.ts b/middleware.ts index 0101fed..c9283b7 100644 --- a/middleware.ts +++ b/middleware.ts @@ -139,7 +139,7 @@ function getPatterns() { handler: adminMiddleware, }, { - pattern: new URLPattern({ pathname: '/auth/*?' }), + pattern: new URLPattern({ pathname: '/auth/update-account' }), handler: async (req: NextRequest, res: NextResponse) => { const { data: { user }, @@ -147,21 +147,27 @@ function getPatterns() { // the user is logged out, so we don't need to do anything if (!user) { - return; + return NextResponse.redirect(new URL('/', req.nextUrl.origin).href); } - // check if we need to verify MFA (user is authenticated but needs to verify MFA) - const isVerifyMfa = req.nextUrl.pathname === pathsConfig.auth.verifyMfa; + const client = createMiddlewareClient(req, res); + const userIsSuperAdmin = await isSuperAdmin(client); - // If user is logged in and does not need to verify MFA, - // redirect to home page. - if (!isVerifyMfa) { - const nextPath = - req.nextUrl.searchParams.get('next') ?? pathsConfig.app.home; + if (userIsSuperAdmin) { + // check if we need to verify MFA (user is authenticated but needs to verify MFA) + const isVerifyMfa = + req.nextUrl.pathname === pathsConfig.auth.verifyMfa; - return NextResponse.redirect( - new URL(nextPath, req.nextUrl.origin).href, - ); + // If user is logged in and does not need to verify MFA, + // redirect to home page. + if (!isVerifyMfa) { + const nextPath = + req.nextUrl.searchParams.get('next') ?? pathsConfig.app.home; + + return NextResponse.redirect( + new URL(nextPath, req.nextUrl.origin).href, + ); + } } }, }, diff --git a/package.json b/package.json index b85c538..45a1b03 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,7 @@ "supabase:db:lint": "supabase db lint", "supabase:db:diff": "supabase db diff", "supabase:deploy": "supabase link --project-ref $SUPABASE_PROJECT_REF && supabase db push", - "supabase:typegen": "pnpm run supabase:typegen:packages && pnpm run supabase:typegen:app", - "supabase:typegen:packages": "supabase gen types typescript --local > ../../packages/supabase/src/database.types.ts", - "supabase:typegen:app": "supabase gen types typescript --local > ./supabase/database.types.ts", + "supabase:typegen": "supabase gen types typescript --local > ./packages/supabase/src/database.types.ts", "supabase:db:dump:local": "supabase db dump --local --data-only", "sync-analysis-groups:dev": "NODE_ENV=local ts-node jobs/sync-analysis-groups.ts", "sync-connected-online:dev": "NODE_ENV=local ts-node jobs/sync-connected-online.ts" @@ -114,4 +112,4 @@ "node": ">=20.0.0", "pnpm": ">=9.0.0" } -} \ No newline at end of file +} diff --git a/packages/features/accounts/src/hooks/use-personal-account-data.ts b/packages/features/accounts/src/hooks/use-personal-account-data.ts index 6a901e5..6c53da9 100644 --- a/packages/features/accounts/src/hooks/use-personal-account-data.ts +++ b/packages/features/accounts/src/hooks/use-personal-account-data.ts @@ -26,7 +26,8 @@ export function usePersonalAccountData( ` id, name, - picture_url + picture_url, + last_name `, ) .eq('primary_owner_user_id', userId) diff --git a/packages/features/admin/src/components/admin-account-page.tsx b/packages/features/admin/src/components/admin-account-page.tsx index ea114be..3fb2269 100644 --- a/packages/features/admin/src/components/admin-account-page.tsx +++ b/packages/features/admin/src/components/admin-account-page.tsx @@ -211,7 +211,6 @@ async function TeamAccountPage(props: {
-
Company Employees diff --git a/packages/features/auth/src/components/sign-in-methods-container.tsx b/packages/features/auth/src/components/sign-in-methods-container.tsx index c2c558e..c6d243e 100644 --- a/packages/features/auth/src/components/sign-in-methods-container.tsx +++ b/packages/features/auth/src/components/sign-in-methods-container.tsx @@ -4,6 +4,8 @@ import { useRouter } from 'next/navigation'; import type { Provider } from '@supabase/supabase-js'; +import { useSupabase } from '@/packages/supabase/src/hooks/use-supabase'; + import { isBrowser } from '@kit/shared/utils'; import { If } from '@kit/ui/if'; import { Separator } from '@kit/ui/separator'; @@ -20,6 +22,7 @@ export function SignInMethodsContainer(props: { callback: string; joinTeam: string; returnPath: string; + updateAccount: string; }; providers: { @@ -28,13 +31,14 @@ export function SignInMethodsContainer(props: { oAuth: Provider[]; }; }) { + const client = useSupabase(); const router = useRouter(); const redirectUrl = isBrowser() ? new URL(props.paths.callback, window?.location.origin).toString() : ''; - const onSignIn = () => { + const onSignIn = async (userId?: string) => { // if the user has an invite token, we should join the team if (props.inviteToken) { const searchParams = new URLSearchParams({ @@ -45,8 +49,28 @@ export function SignInMethodsContainer(props: { router.replace(joinTeamPath); } else { - // otherwise, we should redirect to the return path - router.replace(props.paths.returnPath); + if (!userId) { + router.replace(props.paths.callback); + return; + } + + try { + const { data: hasPersonalCode } = await client.rpc( + 'has_personal_code', + { + account_id: userId, + }, + ); + + if (hasPersonalCode) { + router.replace(props.paths.returnPath); + } else { + router.replace(props.paths.updateAccount); + } + } catch { + router.replace(props.paths.callback); + return; + } } }; diff --git a/packages/features/auth/src/components/sign-up-methods-container.tsx b/packages/features/auth/src/components/sign-up-methods-container.tsx index 3fde6d0..cbb6d61 100644 --- a/packages/features/auth/src/components/sign-up-methods-container.tsx +++ b/packages/features/auth/src/components/sign-up-methods-container.tsx @@ -16,6 +16,7 @@ export function SignUpMethodsContainer(props: { paths: { callback: string; appHome: string; + updateAccount: string; }; providers: { diff --git a/packages/features/auth/src/components/update-account-form.tsx b/packages/features/auth/src/components/update-account-form.tsx new file mode 100644 index 0000000..2cad0f0 --- /dev/null +++ b/packages/features/auth/src/components/update-account-form.tsx @@ -0,0 +1,225 @@ +'use client'; + +import Link from 'next/link'; + +import { User } from '@supabase/supabase-js'; + +import { ExternalLink } from '@/public/assets/external-link'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; + +import { Button } from '@kit/ui/button'; +import { Checkbox } from '@kit/ui/checkbox'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@kit/ui/form'; +import { Input } from '@kit/ui/input'; +import { Trans } from '@kit/ui/trans'; + +import { UpdateAccountSchema } from '../schemas/update-account.schema'; +import { onUpdateAccount } from '../server/actions/update-account-actions'; + +export function UpdateAccountForm({ user }: { user: User }) { + const form = useForm({ + resolver: zodResolver(UpdateAccountSchema), + mode: 'onChange', + defaultValues: { + firstName: '', + lastName: '', + personalCode: '', + email: user.email, + phone: '', + city: '', + weight: 0, + height: 0, + userConsent: false, + }, + }); + return ( +
+ + ( + + + + + + + + + + )} + /> + + ( + + + + + + + + + + )} + /> + + ( + + + + + + + + + + )} + /> + + ( + + + + + + + + + + )} + /> + + ( + + + + + + + + + + )} + /> + + ( + + + + + + + + + + )} + /> + +
+ ( + + + + + + + field.onChange( + e.target.value === '' ? null : Number(e.target.value), + ) + } + /> + + + + )} + /> + + ( + + + + + + + field.onChange( + e.target.value === '' ? null : Number(e.target.value), + ) + } + /> + + + + )} + /> +
+ + ( + +
+ + + + + + +
+ + + + + +
+ )} + /> + + + + + ); +} diff --git a/packages/features/auth/src/schemas/update-account.schema.ts b/packages/features/auth/src/schemas/update-account.schema.ts new file mode 100644 index 0000000..ee1e331 --- /dev/null +++ b/packages/features/auth/src/schemas/update-account.schema.ts @@ -0,0 +1,44 @@ +import { z } from 'zod'; + +export const UpdateAccountSchema = z.object({ + firstName: z + .string({ + required_error: 'First name is required', + }) + .nonempty(), + lastName: z + .string({ + required_error: 'Last name is required', + }) + .nonempty(), + personalCode: z + .string({ + required_error: 'Personal code is required', + }) + .nonempty(), + email: z.string().email({ + message: 'Email is required', + }), + phone: z + .string({ + required_error: 'Phone number is required', + }) + .nonempty(), + city: z.string().optional(), + weight: z + .number({ + required_error: 'Weight is required', + invalid_type_error: 'Weight must be a number', + }) + .gt(0, { message: 'Weight must be greater than 0' }), + + height: z + .number({ + required_error: 'Height is required', + invalid_type_error: 'Height must be a number', + }) + .gt(0, { message: 'Height must be greater than 0' }), + userConsent: z.boolean().refine((val) => val === true, { + message: 'Must be true', + }), +}); diff --git a/packages/features/auth/src/server/actions/update-account-actions.ts b/packages/features/auth/src/server/actions/update-account-actions.ts new file mode 100644 index 0000000..eb24be0 --- /dev/null +++ b/packages/features/auth/src/server/actions/update-account-actions.ts @@ -0,0 +1,44 @@ +'use server'; + +import { redirect } from 'next/navigation'; + +import { enhanceAction } from '@kit/next/actions'; +import { getSupabaseServerClient } from '@kit/supabase/server-client'; + +import pathsConfig from '~/config/paths.config'; + +import { UpdateAccountSchema } from '../../schemas/update-account.schema'; +import { createAuthApi } from '../api'; + +export interface AccountSubmitData { + firstName: string; + lastName: string; + personalCode: string; + email: string; + phone?: string; + city?: string; + weight: number | null; + height: number | null; + userConsent: boolean; +} + +export const onUpdateAccount = enhanceAction( + async (params) => { + const client = getSupabaseServerClient(); + const api = createAuthApi(client); + + try { + await api.updateAccount(params); + console.log('SUCCESS', pathsConfig.auth.updateAccountSuccess); + } catch (err: unknown) { + if (err instanceof Error) { + console.warn('On update account error: ' + err.message); + } + console.warn('On update account error: ', err); + } + redirect(pathsConfig.auth.updateAccountSuccess); + }, + { + schema: UpdateAccountSchema, + }, +); diff --git a/packages/features/auth/src/server/api.ts b/packages/features/auth/src/server/api.ts new file mode 100644 index 0000000..6c10ca1 --- /dev/null +++ b/packages/features/auth/src/server/api.ts @@ -0,0 +1,93 @@ +import { SupabaseClient } from '@supabase/supabase-js'; + +import { Database } from '@kit/supabase/database'; + +import { AccountSubmitData } from './actions/update-account-actions'; + +/** + * Class representing an API for interacting with user accounts. + * @constructor + * @param {SupabaseClient} client - The Supabase client instance. + */ +class AuthApi { + constructor(private readonly client: SupabaseClient) {} + + /** + * @name hasPersonalCode + * @description Check if given account ID has added personal code. + * @param id + */ + async hasPersonalCode(id: string) { + const { data, error } = await this.client.rpc('has_personal_code', { + account_id: id, + }); + + if (error) { + throw error; + } + + return data; + } + + /** + * @name updateAccount + * @description Update required fields for the account. + * @param data + */ + async updateAccount(data: AccountSubmitData) { + const { + data: { user }, + } = await this.client.auth.getUser(); + + if (!user) { + throw new Error('User not authenticated'); + } + + const { error } = await this.client.rpc('update_account', { + p_name: data.firstName, + p_last_name: data.lastName, + p_personal_code: data.personalCode, + p_phone: data.phone || '', + p_city: data.city || '', + p_has_consent_personal_data: data.userConsent, + p_uid: user.id, + }); + + if (error) { + throw error; + } + + if (data.height || data.weight) { + await this.updateAccountParams(data); + } + } + + /** + * @name updateAccountParams + * @description Update account parameters. + * @param data + */ + async updateAccountParams(data: AccountSubmitData) { + const { + data: { user }, + } = await this.client.auth.getUser(); + + if (!user) { + throw new Error('User not authenticated'); + } + console.log('test', user, data); + const response = await this.client.from('account_params').insert({ + account_id: user.id, + height: data.height, + weight: data.weight, + }); + + if (response.error) { + throw response.error; + } + } +} + +export function createAuthApi(client: SupabaseClient) { + return new AuthApi(client); +} diff --git a/packages/features/auth/src/sign-up.ts b/packages/features/auth/src/sign-up.ts index d76b414..6825d4d 100644 --- a/packages/features/auth/src/sign-up.ts +++ b/packages/features/auth/src/sign-up.ts @@ -1,2 +1,3 @@ export * from './components/sign-up-methods-container'; export * from './schemas/password-sign-up.schema'; +export * from './components/update-account-form'; diff --git a/packages/features/auth/tsconfig.json b/packages/features/auth/tsconfig.json index c4697e9..2edb84a 100644 --- a/packages/features/auth/tsconfig.json +++ b/packages/features/auth/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@kit/tsconfig/base.json", + "extends": "../../../tsconfig.json", "compilerOptions": { "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" }, diff --git a/packages/features/notifications/src/components/index.ts b/packages/features/notifications/src/components/index.ts index 9bd47eb..08883b6 100644 --- a/packages/features/notifications/src/components/index.ts +++ b/packages/features/notifications/src/components/index.ts @@ -1 +1,3 @@ export * from './notifications-popover'; +export * from './success-notification'; +export * from './update-account-success-notification'; diff --git a/packages/features/notifications/src/components/success-notification.tsx b/packages/features/notifications/src/components/success-notification.tsx new file mode 100644 index 0000000..7c6be5c --- /dev/null +++ b/packages/features/notifications/src/components/success-notification.tsx @@ -0,0 +1,50 @@ +import Image from 'next/image'; +import Link from 'next/link'; + +import { MedReportLogo } from '@/components/med-report-logo'; + +import { Button } from '@kit/ui/button'; +import { Trans } from '@kit/ui/trans'; + +export const SuccessNotification = ({ + showLogo = true, + title, + titleKey, + descriptionKey, + buttonProps, +}: { + showLogo?: boolean; + title?: string; + titleKey?: string; + descriptionKey?: string; + buttonProps?: { + buttonTitleKey: string; + href: string; + }; +}) => { + return ( +
+ {showLogo && } +
+ Success +

{title || }

+

+ +

+
+ {buttonProps && ( + + )} +
+ ); +}; diff --git a/packages/features/notifications/src/components/update-account-success-notification.tsx b/packages/features/notifications/src/components/update-account-success-notification.tsx new file mode 100644 index 0000000..a15455b --- /dev/null +++ b/packages/features/notifications/src/components/update-account-success-notification.tsx @@ -0,0 +1,39 @@ +'use client'; + +import { redirect } from 'next/navigation'; + +import pathsConfig from '@/config/paths.config'; +import { useTranslation } from 'react-i18next'; + +import { usePersonalAccountData } from '@kit/accounts/hooks/use-personal-account-data'; + +import { SuccessNotification } from './success-notification'; + +export const UpdateAccountSuccessNotification = ({ + userId, +}: { + userId?: string; +}) => { + const { t } = useTranslation('account'); + + if (!userId) { + redirect(pathsConfig.app.home); + } + + const { data: accountData } = usePersonalAccountData(userId); + + return ( + + ); +}; diff --git a/packages/features/notifications/tsconfig.json b/packages/features/notifications/tsconfig.json index 9a53360..e2b4bf5 100644 --- a/packages/features/notifications/tsconfig.json +++ b/packages/features/notifications/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@kit/tsconfig/base.json", + "extends": "../../../tsconfig.json", "compilerOptions": { "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" }, diff --git a/packages/supabase/src/database.types.ts b/packages/supabase/src/database.types.ts index 452ae7b..22971eb 100644 --- a/packages/supabase/src/database.types.ts +++ b/packages/supabase/src/database.types.ts @@ -4,1439 +4,1584 @@ export type Json = | boolean | null | { [key: string]: Json | undefined } - | Json[]; + | Json[] export type Database = { + audit: { + Tables: { + log_entries: { + Row: { + changed_at: string + changed_by: string | null + changed_by_role: string | null + changed_data: Json | null + id: number + operation: string + record_key: number | null + row_data: Json | null + schema_name: string + table_name: string + } + Insert: { + changed_at?: string + changed_by?: string | null + changed_by_role?: string | null + changed_data?: Json | null + id?: number + operation: string + record_key?: number | null + row_data?: Json | null + schema_name: string + table_name: string + } + Update: { + changed_at?: string + changed_by?: string | null + changed_by_role?: string | null + changed_data?: Json | null + id?: number + operation?: string + record_key?: number | null + row_data?: Json | null + schema_name?: string + table_name?: string + } + Relationships: [] + } + sync_entries: { + Row: { + changed_by_role: string + comment: string | null + created_at: string + id: number + operation: string + status: string + } + Insert: { + changed_by_role: string + comment?: string | null + created_at?: string + id?: number + operation: string + status: string + } + Update: { + changed_by_role?: string + comment?: string | null + created_at?: string + id?: number + operation?: string + status?: string + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + [_ in never]: never + } + Enums: { + sync_status: "SUCCESS" | "FAIL" + } + CompositeTypes: { + [_ in never]: never + } + } graphql_public: { Tables: { - [_ in never]: never; - }; + [_ in never]: never + } Views: { - [_ in never]: never; - }; + [_ in never]: never + } Functions: { graphql: { Args: { - operationName?: string; - query?: string; - variables?: Json; - extensions?: Json; - }; - Returns: Json; - }; - }; + operationName?: string + query?: string + variables?: Json + extensions?: Json + } + Returns: Json + } + } Enums: { - [_ in never]: never; - }; + [_ in never]: never + } CompositeTypes: { - [_ in never]: never; - }; - }; + [_ in never]: never + } + } public: { Tables: { + account_params: { + Row: { + account_id: string + height: number | null + id: string + recorded_at: string + weight: number | null + } + Insert: { + account_id?: string + height?: number | null + id?: string + recorded_at?: string + weight?: number | null + } + Update: { + account_id?: string + height?: number | null + id?: string + recorded_at?: string + weight?: number | null + } + Relationships: [] + } accounts: { Row: { - created_at: string | null; - created_by: string | null; - email: string | null; - id: string; - is_personal_account: boolean; - name: string; - picture_url: string | null; - primary_owner_user_id: string; - public_data: Json; - slug: string | null; - updated_at: string | null; - updated_by: string | null; - }; + city: string | null + created_at: string | null + created_by: string | null + email: string | null + has_consent_personal_data: boolean | null + id: string + is_personal_account: boolean + last_name: string | null + name: string + personal_code: string | null + phone: string | null + picture_url: string | null + primary_owner_user_id: string + public_data: Json + slug: string | null + updated_at: string | null + updated_by: string | null + } Insert: { - created_at?: string | null; - created_by?: string | null; - email?: string | null; - id?: string; - is_personal_account?: boolean; - name: string; - picture_url?: string | null; - primary_owner_user_id?: string; - public_data?: Json; - slug?: string | null; - updated_at?: string | null; - updated_by?: string | null; - }; + city?: string | null + created_at?: string | null + created_by?: string | null + email?: string | null + has_consent_personal_data?: boolean | null + id?: string + is_personal_account?: boolean + last_name?: string | null + name: string + personal_code?: string | null + phone?: string | null + picture_url?: string | null + primary_owner_user_id?: string + public_data?: Json + slug?: string | null + updated_at?: string | null + updated_by?: string | null + } Update: { - created_at?: string | null; - created_by?: string | null; - email?: string | null; - id?: string; - is_personal_account?: boolean; - name?: string; - picture_url?: string | null; - primary_owner_user_id?: string; - public_data?: Json; - slug?: string | null; - updated_at?: string | null; - updated_by?: string | null; - }; - Relationships: []; - }; + city?: string | null + created_at?: string | null + created_by?: string | null + email?: string | null + has_consent_personal_data?: boolean | null + id?: string + is_personal_account?: boolean + last_name?: string | null + name?: string + personal_code?: string | null + phone?: string | null + picture_url?: string | null + primary_owner_user_id?: string + public_data?: Json + slug?: string | null + updated_at?: string | null + updated_by?: string | null + } + Relationships: [] + } accounts_memberships: { Row: { - account_id: string; - account_role: string; - created_at: string; - created_by: string | null; - updated_at: string; - updated_by: string | null; - user_id: string; - }; + account_id: string + account_role: string + created_at: string + created_by: string | null + updated_at: string + updated_by: string | null + user_id: string + } Insert: { - account_id: string; - account_role: string; - created_at?: string; - created_by?: string | null; - updated_at?: string; - updated_by?: string | null; - user_id: string; - }; + account_id: string + account_role: string + created_at?: string + created_by?: string | null + updated_at?: string + updated_by?: string | null + user_id: string + } Update: { - account_id?: string; - account_role?: string; - created_at?: string; - created_by?: string | null; - updated_at?: string; - updated_by?: string | null; - user_id?: string; - }; + account_id?: string + account_role?: string + created_at?: string + created_by?: string | null + updated_at?: string + updated_by?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'accounts_memberships_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_account_role_fkey'; - columns: ['account_role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "accounts_memberships_account_role_fkey" + columns: ["account_role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; + ] + } + analyses: { + Row: { + analysis_id_oid: string + analysis_id_original: string + analysis_name_lab: string | null + created_at: string + id: number + order: number + parent_analysis_element_id: number + tehik_loinc_name: string | null + tehik_short_loinc: string | null + updated_at: string | null + } + Insert: { + analysis_id_oid: string + analysis_id_original: string + analysis_name_lab?: string | null + created_at?: string + id?: number + order: number + parent_analysis_element_id: number + tehik_loinc_name?: string | null + tehik_short_loinc?: string | null + updated_at?: string | null + } + Update: { + analysis_id_oid?: string + analysis_id_original?: string + analysis_name_lab?: string | null + created_at?: string + id?: number + order?: number + parent_analysis_element_id?: number + tehik_loinc_name?: string | null + tehik_short_loinc?: string | null + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: "analyses_parent_analysis_element_id_fkey" + columns: ["parent_analysis_element_id"] + isOneToOne: false + referencedRelation: "analysis_elements" + referencedColumns: ["id"] + }, + ] + } + analysis_elements: { + Row: { + analysis_id_oid: string + analysis_id_original: string + analysis_name_lab: string | null + created_at: string + id: number + material_groups: Json[] | null + order: number + parent_analysis_group_id: number + tehik_loinc_name: string + tehik_short_loinc: string + updated_at: string | null + } + Insert: { + analysis_id_oid: string + analysis_id_original: string + analysis_name_lab?: string | null + created_at?: string + id?: number + material_groups?: Json[] | null + order: number + parent_analysis_group_id: number + tehik_loinc_name: string + tehik_short_loinc: string + updated_at?: string | null + } + Update: { + analysis_id_oid?: string + analysis_id_original?: string + analysis_name_lab?: string | null + created_at?: string + id?: number + material_groups?: Json[] | null + order?: number + parent_analysis_group_id?: number + tehik_loinc_name?: string + tehik_short_loinc?: string + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: "analysis_elements_parent_analysis_group_id_fkey" + columns: ["parent_analysis_group_id"] + isOneToOne: false + referencedRelation: "analysis_groups" + referencedColumns: ["id"] + }, + ] + } + analysis_groups: { + Row: { + created_at: string + id: number + name: string + order: number + original_id: string + updated_at: string | null + } + Insert: { + created_at?: string + id?: number + name: string + order: number + original_id: string + updated_at?: string | null + } + Update: { + created_at?: string + id?: number + name?: string + order?: number + original_id?: string + updated_at?: string | null + } + Relationships: [] + } + analysis_orders: { + Row: { + analysis_element_ids: number[] | null + analysis_ids: number[] | null + created_at: string + id: number + status: Database["public"]["Enums"]["analysis_order_status"] + user_id: string + } + Insert: { + analysis_element_ids?: number[] | null + analysis_ids?: number[] | null + created_at?: string + id?: number + status: Database["public"]["Enums"]["analysis_order_status"] + user_id: string + } + Update: { + analysis_element_ids?: number[] | null + analysis_ids?: number[] | null + created_at?: string + id?: number + status?: Database["public"]["Enums"]["analysis_order_status"] + user_id?: string + } + Relationships: [] + } + analysis_response_elements: { + Row: { + analysis_element_original_id: string + analysis_response_id: number + created_at: string + id: number + norm_lower: number | null + norm_lower_included: boolean | null + norm_status: number | null + norm_upper: number | null + norm_upper_included: boolean | null + original_response_element: Json + response_time: string + response_value: Json + unit: string | null + updated_at: string | null + } + Insert: { + analysis_element_original_id: string + analysis_response_id: number + created_at?: string + id?: number + norm_lower?: number | null + norm_lower_included?: boolean | null + norm_status?: number | null + norm_upper?: number | null + norm_upper_included?: boolean | null + original_response_element: Json + response_time: string + response_value: Json + unit?: string | null + updated_at?: string | null + } + Update: { + analysis_element_original_id?: string + analysis_response_id?: number + created_at?: string + id?: number + norm_lower?: number | null + norm_lower_included?: boolean | null + norm_status?: number | null + norm_upper?: number | null + norm_upper_included?: boolean | null + original_response_element?: Json + response_time?: string + response_value?: Json + unit?: string | null + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: "analysis_response_element_analysis_response_id_fkey" + columns: ["analysis_response_id"] + isOneToOne: false + referencedRelation: "analysis_responses" + referencedColumns: ["id"] + }, + ] + } + analysis_responses: { + Row: { + analysis_order_id: number + created_at: string + id: number + order_number: string + order_status: Database["public"]["Enums"]["analysis_order_status"] + updated_at: string | null + user_id: string + } + Insert: { + analysis_order_id: number + created_at?: string + id?: number + order_number: string + order_status: Database["public"]["Enums"]["analysis_order_status"] + updated_at?: string | null + user_id: string + } + Update: { + analysis_order_id?: number + created_at?: string + id?: number + order_number?: string + order_status?: Database["public"]["Enums"]["analysis_order_status"] + updated_at?: string | null + user_id?: string + } + Relationships: [ + { + foreignKeyName: "analysis_responses_analysis_order_id_fkey" + columns: ["analysis_order_id"] + isOneToOne: false + referencedRelation: "analysis_orders" + referencedColumns: ["id"] + }, + ] + } billing_customers: { Row: { - account_id: string; - customer_id: string; - email: string | null; - id: number; - provider: Database['public']['Enums']['billing_provider']; - }; + account_id: string + customer_id: string + email: string | null + id: number + provider: Database["public"]["Enums"]["billing_provider"] + } Insert: { - account_id: string; - customer_id: string; - email?: string | null; - id?: number; - provider: Database['public']['Enums']['billing_provider']; - }; + account_id: string + customer_id: string + email?: string | null + id?: number + provider: Database["public"]["Enums"]["billing_provider"] + } Update: { - account_id?: string; - customer_id?: string; - email?: string | null; - id?: number; - provider?: Database['public']['Enums']['billing_provider']; - }; + account_id?: string + customer_id?: string + email?: string | null + id?: number + provider?: Database["public"]["Enums"]["billing_provider"] + } Relationships: [ { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "billing_customers_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "billing_customers_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "billing_customers_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, - ]; - }; + ] + } + codes: { + Row: { + analysis_element_id: number | null + analysis_group_id: number | null + analysis_id: number | null + coefficient: number + created_at: string + hk_code: string + hk_code_multiplier: number + id: number + price: number + updated_at: string | null + } + Insert: { + analysis_element_id?: number | null + analysis_group_id?: number | null + analysis_id?: number | null + coefficient: number + created_at?: string + hk_code: string + hk_code_multiplier: number + id?: number + price: number + updated_at?: string | null + } + Update: { + analysis_element_id?: number | null + analysis_group_id?: number | null + analysis_id?: number | null + coefficient?: number + created_at?: string + hk_code?: string + hk_code_multiplier?: number + id?: number + price?: number + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: "codes_analysis_element_id_fkey" + columns: ["analysis_element_id"] + isOneToOne: false + referencedRelation: "analysis_elements" + referencedColumns: ["id"] + }, + { + foreignKeyName: "codes_analysis_group_id_fkey" + columns: ["analysis_group_id"] + isOneToOne: false + referencedRelation: "analysis_groups" + referencedColumns: ["id"] + }, + { + foreignKeyName: "codes_analysis_id_fkey" + columns: ["analysis_id"] + isOneToOne: false + referencedRelation: "analyses" + referencedColumns: ["id"] + }, + ] + } config: { Row: { - billing_provider: Database['public']['Enums']['billing_provider']; - enable_account_billing: boolean; - enable_team_account_billing: boolean; - enable_team_accounts: boolean; - }; + billing_provider: Database["public"]["Enums"]["billing_provider"] + enable_account_billing: boolean + enable_team_account_billing: boolean + enable_team_accounts: boolean + } Insert: { - billing_provider?: Database['public']['Enums']['billing_provider']; - enable_account_billing?: boolean; - enable_team_account_billing?: boolean; - enable_team_accounts?: boolean; - }; + billing_provider?: Database["public"]["Enums"]["billing_provider"] + enable_account_billing?: boolean + enable_team_account_billing?: boolean + enable_team_accounts?: boolean + } Update: { - billing_provider?: Database['public']['Enums']['billing_provider']; - enable_account_billing?: boolean; - enable_team_account_billing?: boolean; - enable_team_accounts?: boolean; - }; - Relationships: []; - }; + billing_provider?: Database["public"]["Enums"]["billing_provider"] + enable_account_billing?: boolean + enable_team_account_billing?: boolean + enable_team_accounts?: boolean + } + Relationships: [] + } invitations: { Row: { - account_id: string; - created_at: string; - email: string; - expires_at: string; - id: number; - invite_token: string; - invited_by: string; - role: string; - updated_at: string; - }; + account_id: string + created_at: string + email: string + expires_at: string + id: number + invite_token: string + invited_by: string + role: string + updated_at: string + } Insert: { - account_id: string; - created_at?: string; - email: string; - expires_at?: string; - id?: number; - invite_token: string; - invited_by: string; - role: string; - updated_at?: string; - }; + account_id: string + created_at?: string + email: string + expires_at?: string + id?: number + invite_token: string + invited_by: string + role: string + updated_at?: string + } Update: { - account_id?: string; - created_at?: string; - email?: string; - expires_at?: string; - id?: number; - invite_token?: string; - invited_by?: string; - role?: string; - updated_at?: string; - }; + account_id?: string + created_at?: string + email?: string + expires_at?: string + id?: number + invite_token?: string + invited_by?: string + role?: string + updated_at?: string + } Relationships: [ { - foreignKeyName: 'invitations_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "invitations_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "invitations_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "invitations_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "invitations_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; + ] + } nonces: { Row: { - client_token: string; - created_at: string; - description: string | null; - expires_at: string; - id: string; - last_verification_at: string | null; - last_verification_ip: unknown | null; - last_verification_user_agent: string | null; - metadata: Json | null; - nonce: string; - purpose: string; - revoked: boolean; - revoked_reason: string | null; - scopes: string[] | null; - tags: string[] | null; - used_at: string | null; - user_id: string | null; - verification_attempts: number; - }; + client_token: string + created_at: string + expires_at: string + id: string + last_verification_at: string | null + last_verification_ip: unknown | null + last_verification_user_agent: string | null + metadata: Json | null + nonce: string + purpose: string + revoked: boolean + revoked_reason: string | null + scopes: string[] | null + used_at: string | null + user_id: string | null + verification_attempts: number + } Insert: { - client_token: string; - created_at?: string; - description?: string | null; - expires_at: string; - id?: string; - last_verification_at?: string | null; - last_verification_ip?: unknown | null; - last_verification_user_agent?: string | null; - metadata?: Json | null; - nonce: string; - purpose: string; - revoked?: boolean; - revoked_reason?: string | null; - scopes?: string[] | null; - tags?: string[] | null; - used_at?: string | null; - user_id?: string | null; - verification_attempts?: number; - }; + client_token: string + created_at?: string + expires_at: string + id?: string + last_verification_at?: string | null + last_verification_ip?: unknown | null + last_verification_user_agent?: string | null + metadata?: Json | null + nonce: string + purpose: string + revoked?: boolean + revoked_reason?: string | null + scopes?: string[] | null + used_at?: string | null + user_id?: string | null + verification_attempts?: number + } Update: { - client_token?: string; - created_at?: string; - description?: string | null; - expires_at?: string; - id?: string; - last_verification_at?: string | null; - last_verification_ip?: unknown | null; - last_verification_user_agent?: string | null; - metadata?: Json | null; - nonce?: string; - purpose?: string; - revoked?: boolean; - revoked_reason?: string | null; - scopes?: string[] | null; - tags?: string[] | null; - used_at?: string | null; - user_id?: string | null; - verification_attempts?: number; - }; - Relationships: []; - }; + client_token?: string + created_at?: string + expires_at?: string + id?: string + last_verification_at?: string | null + last_verification_ip?: unknown | null + last_verification_user_agent?: string | null + metadata?: Json | null + nonce?: string + purpose?: string + revoked?: boolean + revoked_reason?: string | null + scopes?: string[] | null + used_at?: string | null + user_id?: string | null + verification_attempts?: number + } + Relationships: [] + } notifications: { Row: { - account_id: string; - body: string; - channel: Database['public']['Enums']['notification_channel']; - created_at: string; - dismissed: boolean; - expires_at: string | null; - id: number; - link: string | null; - type: Database['public']['Enums']['notification_type']; - }; + account_id: string + body: string + channel: Database["public"]["Enums"]["notification_channel"] + created_at: string + dismissed: boolean + expires_at: string | null + id: number + link: string | null + type: Database["public"]["Enums"]["notification_type"] + } Insert: { - account_id: string; - body: string; - channel?: Database['public']['Enums']['notification_channel']; - created_at?: string; - dismissed?: boolean; - expires_at?: string | null; - id?: never; - link?: string | null; - type?: Database['public']['Enums']['notification_type']; - }; + account_id: string + body: string + channel?: Database["public"]["Enums"]["notification_channel"] + created_at?: string + dismissed?: boolean + expires_at?: string | null + id?: never + link?: string | null + type?: Database["public"]["Enums"]["notification_type"] + } Update: { - account_id?: string; - body?: string; - channel?: Database['public']['Enums']['notification_channel']; - created_at?: string; - dismissed?: boolean; - expires_at?: string | null; - id?: never; - link?: string | null; - type?: Database['public']['Enums']['notification_type']; - }; + account_id?: string + body?: string + channel?: Database["public"]["Enums"]["notification_channel"] + created_at?: string + dismissed?: boolean + expires_at?: string | null + id?: never + link?: string | null + type?: Database["public"]["Enums"]["notification_type"] + } Relationships: [ { - foreignKeyName: 'notifications_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "notifications_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'notifications_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "notifications_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'notifications_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "notifications_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, - ]; - }; + ] + } order_items: { Row: { - created_at: string; - id: string; - order_id: string; - price_amount: number | null; - product_id: string; - quantity: number; - updated_at: string; - variant_id: string; - }; + created_at: string + id: string + order_id: string + price_amount: number | null + product_id: string + quantity: number + updated_at: string + variant_id: string + } Insert: { - created_at?: string; - id: string; - order_id: string; - price_amount?: number | null; - product_id: string; - quantity?: number; - updated_at?: string; - variant_id: string; - }; + created_at?: string + id: string + order_id: string + price_amount?: number | null + product_id: string + quantity?: number + updated_at?: string + variant_id: string + } Update: { - created_at?: string; - id?: string; - order_id?: string; - price_amount?: number | null; - product_id?: string; - quantity?: number; - updated_at?: string; - variant_id?: string; - }; + created_at?: string + id?: string + order_id?: string + price_amount?: number | null + product_id?: string + quantity?: number + updated_at?: string + variant_id?: string + } Relationships: [ { - foreignKeyName: 'order_items_order_id_fkey'; - columns: ['order_id']; - isOneToOne: false; - referencedRelation: 'orders'; - referencedColumns: ['id']; + foreignKeyName: "order_items_order_id_fkey" + columns: ["order_id"] + isOneToOne: false + referencedRelation: "orders" + referencedColumns: ["id"] }, - ]; - }; + ] + } orders: { Row: { - account_id: string; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - created_at: string; - currency: string; - id: string; - status: Database['public']['Enums']['payment_status']; - total_amount: number; - updated_at: string; - }; + account_id: string + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + created_at: string + currency: string + id: string + status: Database["public"]["Enums"]["payment_status"] + total_amount: number + updated_at: string + } Insert: { - account_id: string; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - created_at?: string; - currency: string; - id: string; - status: Database['public']['Enums']['payment_status']; - total_amount: number; - updated_at?: string; - }; + account_id: string + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + created_at?: string + currency: string + id: string + status: Database["public"]["Enums"]["payment_status"] + total_amount: number + updated_at?: string + } Update: { - account_id?: string; - billing_customer_id?: number; - billing_provider?: Database['public']['Enums']['billing_provider']; - created_at?: string; - currency?: string; - id?: string; - status?: Database['public']['Enums']['payment_status']; - total_amount?: number; - updated_at?: string; - }; + account_id?: string + billing_customer_id?: number + billing_provider?: Database["public"]["Enums"]["billing_provider"] + created_at?: string + currency?: string + id?: string + status?: Database["public"]["Enums"]["payment_status"] + total_amount?: number + updated_at?: string + } Relationships: [ { - foreignKeyName: 'orders_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "orders_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'orders_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "orders_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'orders_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "orders_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'orders_billing_customer_id_fkey'; - columns: ['billing_customer_id']; - isOneToOne: false; - referencedRelation: 'billing_customers'; - referencedColumns: ['id']; + foreignKeyName: "orders_billing_customer_id_fkey" + columns: ["billing_customer_id"] + isOneToOne: false + referencedRelation: "billing_customers" + referencedColumns: ["id"] }, - ]; - }; + ] + } role_permissions: { Row: { - id: number; - permission: Database['public']['Enums']['app_permissions']; - role: string; - }; + id: number + permission: Database["public"]["Enums"]["app_permissions"] + role: string + } Insert: { - id?: number; - permission: Database['public']['Enums']['app_permissions']; - role: string; - }; + id?: number + permission: Database["public"]["Enums"]["app_permissions"] + role: string + } Update: { - id?: number; - permission?: Database['public']['Enums']['app_permissions']; - role?: string; - }; + id?: number + permission?: Database["public"]["Enums"]["app_permissions"] + role?: string + } Relationships: [ { - foreignKeyName: 'role_permissions_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "role_permissions_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; + ] + } roles: { Row: { - hierarchy_level: number; - name: string; - }; + hierarchy_level: number + name: string + } Insert: { - hierarchy_level: number; - name: string; - }; + hierarchy_level: number + name: string + } Update: { - hierarchy_level?: number; - name?: string; - }; - Relationships: []; - }; + hierarchy_level?: number + name?: string + } + Relationships: [] + } subscription_items: { Row: { - created_at: string; - id: string; - interval: string; - interval_count: number; - price_amount: number | null; - product_id: string; - quantity: number; - subscription_id: string; - type: Database['public']['Enums']['subscription_item_type']; - updated_at: string; - variant_id: string; - }; + created_at: string + id: string + interval: string + interval_count: number + price_amount: number | null + product_id: string + quantity: number + subscription_id: string + type: Database["public"]["Enums"]["subscription_item_type"] + updated_at: string + variant_id: string + } Insert: { - created_at?: string; - id: string; - interval: string; - interval_count: number; - price_amount?: number | null; - product_id: string; - quantity?: number; - subscription_id: string; - type: Database['public']['Enums']['subscription_item_type']; - updated_at?: string; - variant_id: string; - }; + created_at?: string + id: string + interval: string + interval_count: number + price_amount?: number | null + product_id: string + quantity?: number + subscription_id: string + type: Database["public"]["Enums"]["subscription_item_type"] + updated_at?: string + variant_id: string + } Update: { - created_at?: string; - id?: string; - interval?: string; - interval_count?: number; - price_amount?: number | null; - product_id?: string; - quantity?: number; - subscription_id?: string; - type?: Database['public']['Enums']['subscription_item_type']; - updated_at?: string; - variant_id?: string; - }; + created_at?: string + id?: string + interval?: string + interval_count?: number + price_amount?: number | null + product_id?: string + quantity?: number + subscription_id?: string + type?: Database["public"]["Enums"]["subscription_item_type"] + updated_at?: string + variant_id?: string + } Relationships: [ { - foreignKeyName: 'subscription_items_subscription_id_fkey'; - columns: ['subscription_id']; - isOneToOne: false; - referencedRelation: 'subscriptions'; - referencedColumns: ['id']; + foreignKeyName: "subscription_items_subscription_id_fkey" + columns: ["subscription_id"] + isOneToOne: false + referencedRelation: "subscriptions" + referencedColumns: ["id"] }, - ]; - }; + ] + } subscriptions: { Row: { - account_id: string; - active: boolean; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - created_at: string; - currency: string; - id: string; - period_ends_at: string; - period_starts_at: string; - status: Database['public']['Enums']['subscription_status']; - trial_ends_at: string | null; - trial_starts_at: string | null; - updated_at: string; - }; + account_id: string + active: boolean + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + created_at: string + currency: string + id: string + period_ends_at: string + period_starts_at: string + status: Database["public"]["Enums"]["subscription_status"] + trial_ends_at: string | null + trial_starts_at: string | null + updated_at: string + } Insert: { - account_id: string; - active: boolean; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - created_at?: string; - currency: string; - id: string; - period_ends_at: string; - period_starts_at: string; - status: Database['public']['Enums']['subscription_status']; - trial_ends_at?: string | null; - trial_starts_at?: string | null; - updated_at?: string; - }; + account_id: string + active: boolean + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + created_at?: string + currency: string + id: string + period_ends_at: string + period_starts_at: string + status: Database["public"]["Enums"]["subscription_status"] + trial_ends_at?: string | null + trial_starts_at?: string | null + updated_at?: string + } Update: { - account_id?: string; - active?: boolean; - billing_customer_id?: number; - billing_provider?: Database['public']['Enums']['billing_provider']; - cancel_at_period_end?: boolean; - created_at?: string; - currency?: string; - id?: string; - period_ends_at?: string; - period_starts_at?: string; - status?: Database['public']['Enums']['subscription_status']; - trial_ends_at?: string | null; - trial_starts_at?: string | null; - updated_at?: string; - }; + account_id?: string + active?: boolean + billing_customer_id?: number + billing_provider?: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end?: boolean + created_at?: string + currency?: string + id?: string + period_ends_at?: string + period_starts_at?: string + status?: Database["public"]["Enums"]["subscription_status"] + trial_ends_at?: string | null + trial_starts_at?: string | null + updated_at?: string + } Relationships: [ { - foreignKeyName: 'subscriptions_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'subscriptions_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'subscriptions_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'subscriptions_billing_customer_id_fkey'; - columns: ['billing_customer_id']; - isOneToOne: false; - referencedRelation: 'billing_customers'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_billing_customer_id_fkey" + columns: ["billing_customer_id"] + isOneToOne: false + referencedRelation: "billing_customers" + referencedColumns: ["id"] }, - ]; - }; - }; + ] + } + } Views: { user_account_workspace: { Row: { - id: string | null; - name: string | null; - picture_url: string | null; + id: string | null + name: string | null + picture_url: string | null subscription_status: - | Database['public']['Enums']['subscription_status'] - | null; - }; - Relationships: []; - }; + | Database["public"]["Enums"]["subscription_status"] + | null + } + Relationships: [] + } user_accounts: { Row: { - id: string | null; - name: string | null; - picture_url: string | null; - role: string | null; - slug: string | null; - }; + id: string | null + name: string | null + picture_url: string | null + role: string | null + slug: string | null + } Relationships: [ { - foreignKeyName: 'accounts_memberships_account_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "accounts_memberships_account_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; - }; + ] + } + } Functions: { accept_invitation: { - Args: { - token: string; - user_id: string; - }; - Returns: string; - }; + Args: { token: string; user_id: string } + Returns: string + } add_invitations_to_account: { Args: { - account_slug: string; - invitations: Database['public']['CompositeTypes']['invitation'][]; - }; - Returns: Database['public']['Tables']['invitations']['Row'][]; - }; + account_slug: string + invitations: Database["public"]["CompositeTypes"]["invitation"][] + } + Returns: Database["public"]["Tables"]["invitations"]["Row"][] + } can_action_account_member: { - Args: { - target_team_account_id: string; - target_user_id: string; - }; - Returns: boolean; - }; + Args: { target_team_account_id: string; target_user_id: string } + Returns: boolean + } create_invitation: { - Args: { - account_id: string; - email: string; - role: string; - }; + Args: { account_id: string; email: string; role: string } Returns: { - account_id: string; - created_at: string; - email: string; - expires_at: string; - id: number; - invite_token: string; - invited_by: string; - role: string; - updated_at: string; - }; - }; + account_id: string + created_at: string + email: string + expires_at: string + id: number + invite_token: string + invited_by: string + role: string + updated_at: string + } + } create_nonce: { Args: { - p_user_id?: string; - p_purpose?: string; - p_expires_in_seconds?: number; - p_metadata?: Json; - p_description?: string; - p_tags?: string[]; - p_scopes?: string[]; - p_revoke_previous?: boolean; - }; - Returns: Json; - }; + p_user_id?: string + p_purpose?: string + p_expires_in_seconds?: number + p_metadata?: Json + p_scopes?: string[] + p_revoke_previous?: boolean + } + Returns: Json + } create_team_account: { - Args: { - account_name: string; - }; + Args: { account_name: string } Returns: { - created_at: string | null; - created_by: string | null; - email: string | null; - id: string; - is_personal_account: boolean; - name: string; - picture_url: string | null; - primary_owner_user_id: string; - public_data: Json; - slug: string | null; - updated_at: string | null; - updated_by: string | null; - }; - }; + city: string | null + created_at: string | null + created_by: string | null + email: string | null + has_consent_personal_data: boolean | null + id: string + is_personal_account: boolean + last_name: string | null + name: string + personal_code: string | null + phone: string | null + picture_url: string | null + primary_owner_user_id: string + public_data: Json + slug: string | null + updated_at: string | null + updated_by: string | null + } + } get_account_invitations: { - Args: { - account_slug: string; - }; + Args: { account_slug: string } Returns: { - id: number; - email: string; - account_id: string; - invited_by: string; - role: string; - created_at: string; - updated_at: string; - expires_at: string; - inviter_name: string; - inviter_email: string; - }[]; - }; + id: number + email: string + account_id: string + invited_by: string + role: string + created_at: string + updated_at: string + expires_at: string + inviter_name: string + inviter_email: string + }[] + } get_account_members: { - Args: { - account_slug: string; - }; + Args: { account_slug: string } Returns: { - id: string; - user_id: string; - account_id: string; - role: string; - role_hierarchy_level: number; - primary_owner_user_id: string; - name: string; - email: string; - picture_url: string; - created_at: string; - updated_at: string; - }[]; - }; + id: string + user_id: string + account_id: string + role: string + role_hierarchy_level: number + primary_owner_user_id: string + name: string + email: string + picture_url: string + created_at: string + updated_at: string + }[] + } get_config: { - Args: Record; - Returns: Json; - }; + Args: Record + Returns: Json + } get_nonce_status: { - Args: { - p_id: string; - }; - Returns: Json; - }; + Args: { p_id: string } + Returns: Json + } get_upper_system_role: { - Args: Record; - Returns: string; - }; + Args: Record + Returns: string + } has_active_subscription: { - Args: { - target_account_id: string; - }; - Returns: boolean; - }; + Args: { target_account_id: string } + Returns: boolean + } has_more_elevated_role: { Args: { - target_user_id: string; - target_account_id: string; - role_name: string; - }; - Returns: boolean; - }; + target_user_id: string + target_account_id: string + role_name: string + } + Returns: boolean + } has_permission: { Args: { - user_id: string; - account_id: string; - permission_name: Database['public']['Enums']['app_permissions']; - }; - Returns: boolean; - }; + user_id: string + account_id: string + permission_name: Database["public"]["Enums"]["app_permissions"] + } + Returns: boolean + } + has_personal_code: { + Args: { account_id: string } + Returns: boolean + } has_role_on_account: { - Args: { - account_id: string; - account_role?: string; - }; - Returns: boolean; - }; + Args: { account_id: string; account_role?: string } + Returns: boolean + } has_same_role_hierarchy_level: { Args: { - target_user_id: string; - target_account_id: string; - role_name: string; - }; - Returns: boolean; - }; - install_extensions: { - Args: Record; - Returns: undefined; - }; + target_user_id: string + target_account_id: string + role_name: string + } + Returns: boolean + } is_aal2: { - Args: Record; - Returns: boolean; - }; + Args: Record + Returns: boolean + } is_account_owner: { - Args: { - account_id: string; - }; - Returns: boolean; - }; + Args: { account_id: string } + Returns: boolean + } is_account_team_member: { - Args: { - target_account_id: string; - }; - Returns: boolean; - }; + Args: { target_account_id: string } + Returns: boolean + } is_mfa_compliant: { - Args: Record; - Returns: boolean; - }; + Args: Record + Returns: boolean + } is_set: { - Args: { - field_name: string; - }; - Returns: boolean; - }; + Args: { field_name: string } + Returns: boolean + } is_super_admin: { - Args: Record; - Returns: boolean; - }; + Args: Record + Returns: boolean + } is_team_member: { - Args: { - account_id: string; - user_id: string; - }; - Returns: boolean; - }; + Args: { account_id: string; user_id: string } + Returns: boolean + } revoke_nonce: { - Args: { - p_id: string; - p_reason?: string; - }; - Returns: boolean; - }; + Args: { p_id: string; p_reason?: string } + Returns: boolean + } team_account_workspace: { - Args: { - account_slug: string; - }; + Args: { account_slug: string } Returns: { - id: string; - name: string; - picture_url: string; - slug: string; - role: string; - role_hierarchy_level: number; - primary_owner_user_id: string; - subscription_status: Database['public']['Enums']['subscription_status']; - permissions: Database['public']['Enums']['app_permissions'][]; - }[]; - }; + id: string + name: string + picture_url: string + slug: string + role: string + role_hierarchy_level: number + primary_owner_user_id: string + subscription_status: Database["public"]["Enums"]["subscription_status"] + permissions: Database["public"]["Enums"]["app_permissions"][] + }[] + } transfer_team_account_ownership: { + Args: { target_account_id: string; new_owner_id: string } + Returns: undefined + } + update_account: { Args: { - target_account_id: string; - new_owner_id: string; - }; - Returns: undefined; - }; + p_name: string + p_last_name: string + p_personal_code: string + p_phone: string + p_city: string + p_has_consent_personal_data: boolean + p_uid: string + } + Returns: undefined + } upsert_order: { Args: { - target_account_id: string; - target_customer_id: string; - target_order_id: string; - status: Database['public']['Enums']['payment_status']; - billing_provider: Database['public']['Enums']['billing_provider']; - total_amount: number; - currency: string; - line_items: Json; - }; + target_account_id: string + target_customer_id: string + target_order_id: string + status: Database["public"]["Enums"]["payment_status"] + billing_provider: Database["public"]["Enums"]["billing_provider"] + total_amount: number + currency: string + line_items: Json + } Returns: { - account_id: string; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - created_at: string; - currency: string; - id: string; - status: Database['public']['Enums']['payment_status']; - total_amount: number; - updated_at: string; - }; - }; + account_id: string + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + created_at: string + currency: string + id: string + status: Database["public"]["Enums"]["payment_status"] + total_amount: number + updated_at: string + } + } upsert_subscription: { Args: { - target_account_id: string; - target_customer_id: string; - target_subscription_id: string; - active: boolean; - status: Database['public']['Enums']['subscription_status']; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - currency: string; - period_starts_at: string; - period_ends_at: string; - line_items: Json; - trial_starts_at?: string; - trial_ends_at?: string; - }; + target_account_id: string + target_customer_id: string + target_subscription_id: string + active: boolean + status: Database["public"]["Enums"]["subscription_status"] + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + currency: string + period_starts_at: string + period_ends_at: string + line_items: Json + trial_starts_at?: string + trial_ends_at?: string + } Returns: { - account_id: string; - active: boolean; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - created_at: string; - currency: string; - id: string; - period_ends_at: string; - period_starts_at: string; - status: Database['public']['Enums']['subscription_status']; - trial_ends_at: string | null; - trial_starts_at: string | null; - updated_at: string; - }; - }; + account_id: string + active: boolean + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + created_at: string + currency: string + id: string + period_ends_at: string + period_starts_at: string + status: Database["public"]["Enums"]["subscription_status"] + trial_ends_at: string | null + trial_starts_at: string | null + updated_at: string + } + } verify_nonce: { Args: { - p_token: string; - p_purpose: string; - p_user_id?: string; - p_required_scopes?: string[]; - p_max_verification_attempts?: number; - p_ip?: unknown; - p_user_agent?: string; - }; - Returns: Json; - }; - }; + p_token: string + p_purpose: string + p_user_id?: string + p_required_scopes?: string[] + p_max_verification_attempts?: number + p_ip?: unknown + p_user_agent?: string + } + Returns: Json + } + } Enums: { + analysis_order_status: + | "QUEUED" + | "ON_HOLD" + | "PROCESSING" + | "COMPLETED" + | "REJECTED" + | "CANCELLED" app_permissions: - | 'roles.manage' - | 'billing.manage' - | 'settings.manage' - | 'members.manage' - | 'invites.manage'; - billing_provider: 'stripe' | 'lemon-squeezy' | 'paddle'; - notification_channel: 'in_app' | 'email'; - notification_type: 'info' | 'warning' | 'error'; - payment_status: 'pending' | 'succeeded' | 'failed'; - subscription_item_type: 'flat' | 'per_seat' | 'metered'; + | "roles.manage" + | "billing.manage" + | "settings.manage" + | "members.manage" + | "invites.manage" + billing_provider: "stripe" | "lemon-squeezy" | "paddle" + notification_channel: "in_app" | "email" + notification_type: "info" | "warning" | "error" + payment_status: "pending" | "succeeded" | "failed" + subscription_item_type: "flat" | "per_seat" | "metered" subscription_status: - | 'active' - | 'trialing' - | 'past_due' - | 'canceled' - | 'unpaid' - | 'incomplete' - | 'incomplete_expired' - | 'paused'; - }; + | "active" + | "trialing" + | "past_due" + | "canceled" + | "unpaid" + | "incomplete" + | "incomplete_expired" + | "paused" + } CompositeTypes: { invitation: { - email: string | null; - role: string | null; - }; - }; - }; - storage: { - Tables: { - buckets: { - Row: { - allowed_mime_types: string[] | null; - avif_autodetection: boolean | null; - created_at: string | null; - file_size_limit: number | null; - id: string; - name: string; - owner: string | null; - owner_id: string | null; - public: boolean | null; - updated_at: string | null; - }; - Insert: { - allowed_mime_types?: string[] | null; - avif_autodetection?: boolean | null; - created_at?: string | null; - file_size_limit?: number | null; - id: string; - name: string; - owner?: string | null; - owner_id?: string | null; - public?: boolean | null; - updated_at?: string | null; - }; - Update: { - allowed_mime_types?: string[] | null; - avif_autodetection?: boolean | null; - created_at?: string | null; - file_size_limit?: number | null; - id?: string; - name?: string; - owner?: string | null; - owner_id?: string | null; - public?: boolean | null; - updated_at?: string | null; - }; - Relationships: []; - }; - migrations: { - Row: { - executed_at: string | null; - hash: string; - id: number; - name: string; - }; - Insert: { - executed_at?: string | null; - hash: string; - id: number; - name: string; - }; - Update: { - executed_at?: string | null; - hash?: string; - id?: number; - name?: string; - }; - Relationships: []; - }; - objects: { - Row: { - bucket_id: string | null; - created_at: string | null; - id: string; - last_accessed_at: string | null; - metadata: Json | null; - name: string | null; - owner: string | null; - owner_id: string | null; - path_tokens: string[] | null; - updated_at: string | null; - user_metadata: Json | null; - version: string | null; - }; - Insert: { - bucket_id?: string | null; - created_at?: string | null; - id?: string; - last_accessed_at?: string | null; - metadata?: Json | null; - name?: string | null; - owner?: string | null; - owner_id?: string | null; - path_tokens?: string[] | null; - updated_at?: string | null; - user_metadata?: Json | null; - version?: string | null; - }; - Update: { - bucket_id?: string | null; - created_at?: string | null; - id?: string; - last_accessed_at?: string | null; - metadata?: Json | null; - name?: string | null; - owner?: string | null; - owner_id?: string | null; - path_tokens?: string[] | null; - updated_at?: string | null; - user_metadata?: Json | null; - version?: string | null; - }; - Relationships: [ - { - foreignKeyName: 'objects_bucketId_fkey'; - columns: ['bucket_id']; - isOneToOne: false; - referencedRelation: 'buckets'; - referencedColumns: ['id']; - }, - ]; - }; - s3_multipart_uploads: { - Row: { - bucket_id: string; - created_at: string; - id: string; - in_progress_size: number; - key: string; - owner_id: string | null; - upload_signature: string; - user_metadata: Json | null; - version: string; - }; - Insert: { - bucket_id: string; - created_at?: string; - id: string; - in_progress_size?: number; - key: string; - owner_id?: string | null; - upload_signature: string; - user_metadata?: Json | null; - version: string; - }; - Update: { - bucket_id?: string; - created_at?: string; - id?: string; - in_progress_size?: number; - key?: string; - owner_id?: string | null; - upload_signature?: string; - user_metadata?: Json | null; - version?: string; - }; - Relationships: [ - { - foreignKeyName: 's3_multipart_uploads_bucket_id_fkey'; - columns: ['bucket_id']; - isOneToOne: false; - referencedRelation: 'buckets'; - referencedColumns: ['id']; - }, - ]; - }; - s3_multipart_uploads_parts: { - Row: { - bucket_id: string; - created_at: string; - etag: string; - id: string; - key: string; - owner_id: string | null; - part_number: number; - size: number; - upload_id: string; - version: string; - }; - Insert: { - bucket_id: string; - created_at?: string; - etag: string; - id?: string; - key: string; - owner_id?: string | null; - part_number: number; - size?: number; - upload_id: string; - version: string; - }; - Update: { - bucket_id?: string; - created_at?: string; - etag?: string; - id?: string; - key?: string; - owner_id?: string | null; - part_number?: number; - size?: number; - upload_id?: string; - version?: string; - }; - Relationships: [ - { - foreignKeyName: 's3_multipart_uploads_parts_bucket_id_fkey'; - columns: ['bucket_id']; - isOneToOne: false; - referencedRelation: 'buckets'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 's3_multipart_uploads_parts_upload_id_fkey'; - columns: ['upload_id']; - isOneToOne: false; - referencedRelation: 's3_multipart_uploads'; - referencedColumns: ['id']; - }, - ]; - }; - }; - Views: { - [_ in never]: never; - }; - Functions: { - can_insert_object: { - Args: { - bucketid: string; - name: string; - owner: string; - metadata: Json; - }; - Returns: undefined; - }; - extension: { - Args: { - name: string; - }; - Returns: string; - }; - filename: { - Args: { - name: string; - }; - Returns: string; - }; - foldername: { - Args: { - name: string; - }; - Returns: string[]; - }; - get_size_by_bucket: { - Args: Record; - Returns: { - size: number; - bucket_id: string; - }[]; - }; - list_multipart_uploads_with_delimiter: { - Args: { - bucket_id: string; - prefix_param: string; - delimiter_param: string; - max_keys?: number; - next_key_token?: string; - next_upload_token?: string; - }; - Returns: { - key: string; - id: string; - created_at: string; - }[]; - }; - list_objects_with_delimiter: { - Args: { - bucket_id: string; - prefix_param: string; - delimiter_param: string; - max_keys?: number; - start_after?: string; - next_token?: string; - }; - Returns: { - name: string; - id: string; - metadata: Json; - updated_at: string; - }[]; - }; - operation: { - Args: Record; - Returns: string; - }; - search: { - Args: { - prefix: string; - bucketname: string; - limits?: number; - levels?: number; - offsets?: number; - search?: string; - sortcolumn?: string; - sortorder?: string; - }; - Returns: { - name: string; - id: string; - updated_at: string; - created_at: string; - last_accessed_at: string; - metadata: Json; - }[]; - }; - }; - Enums: { - [_ in never]: never; - }; - CompositeTypes: { - [_ in never]: never; - }; - }; -}; + email: string | null + role: string | null + } + } + } +} -type PublicSchema = Database[Extract]; +type DefaultSchema = Database[Extract] export type Tables< - PublicTableNameOrOptions extends - | keyof (PublicSchema['Tables'] & PublicSchema['Views']) + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views']) + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof Database + } + ? keyof (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends { - Row: infer R; +> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } + ? (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R } ? R : never - : PublicTableNameOrOptions extends keyof (PublicSchema['Tables'] & - PublicSchema['Views']) - ? (PublicSchema['Tables'] & - PublicSchema['Views'])[PublicTableNameOrOptions] extends { - Row: infer R; + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R } ? R : never - : never; + : never export type TablesInsert< - PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof Database + } + ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { - Insert: infer I; +> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } + ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I } ? I : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { - Insert: infer I; + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I } ? I : never - : never; + : never export type TablesUpdate< - PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof Database + } + ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { - Update: infer U; +> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } + ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U } ? U : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { - Update: infer U; + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U } ? U : never - : never; + : never export type Enums< - PublicEnumNameOrOptions extends - | keyof PublicSchema['Enums'] + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] | { schema: keyof Database }, - EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums'] + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof Database + } + ? keyof Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] : never = never, -> = PublicEnumNameOrOptions extends { schema: keyof Database } - ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName] - : PublicEnumNameOrOptions extends keyof PublicSchema['Enums'] - ? PublicSchema['Enums'][PublicEnumNameOrOptions] - : never; +> = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database } + ? Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never export type CompositeTypes< PublicCompositeTypeNameOrOptions extends - | keyof PublicSchema['CompositeTypes'] + | keyof DefaultSchema["CompositeTypes"] | { schema: keyof Database }, CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof Database; + schema: keyof Database } - ? keyof Database[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'] + ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] : never = never, > = PublicCompositeTypeNameOrOptions extends { schema: keyof Database } - ? Database[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName] - : PublicCompositeTypeNameOrOptions extends keyof PublicSchema['CompositeTypes'] - ? PublicSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions] - : never; + ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + audit: { + Enums: { + sync_status: ["SUCCESS", "FAIL"], + }, + }, + graphql_public: { + Enums: {}, + }, + public: { + Enums: { + analysis_order_status: [ + "QUEUED", + "ON_HOLD", + "PROCESSING", + "COMPLETED", + "REJECTED", + "CANCELLED", + ], + app_permissions: [ + "roles.manage", + "billing.manage", + "settings.manage", + "members.manage", + "invites.manage", + ], + billing_provider: ["stripe", "lemon-squeezy", "paddle"], + notification_channel: ["in_app", "email"], + notification_type: ["info", "warning", "error"], + payment_status: ["pending", "succeeded", "failed"], + subscription_item_type: ["flat", "per_seat", "metered"], + subscription_status: [ + "active", + "trialing", + "past_due", + "canceled", + "unpaid", + "incomplete", + "incomplete_expired", + "paused", + ], + }, + }, +} as const + diff --git a/packages/ui/src/makerkit/version-updater.tsx b/packages/ui/src/makerkit/version-updater.tsx index 28bdbb8..5bb3877 100644 --- a/packages/ui/src/makerkit/version-updater.tsx +++ b/packages/ui/src/makerkit/version-updater.tsx @@ -70,7 +70,7 @@ export function VersionUpdater(props: { intervalTimeInSecond?: number }) { setDismissed(true); }} > - +