From 291919c2d19397584e93eba1c71806fe0b12a5cb Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Tue, 17 Jun 2025 13:45:46 +0300 Subject: [PATCH 1/5] refactor: update company registration flow and email handling; switch to zod for validation --- app/(public)/register-company/page.tsx | 91 +++++++++++-------- .../register-company/success/page.tsx | 26 ++++-- lib/dev-mock-modules.ts | 2 +- lib/services/mailer.service.ts | 42 +++++++++ lib/services/register-company.service.ts | 39 ++++---- lib/validations/companySchema.ts | 20 ++-- lib/validations/email.schema.ts | 7 ++ next.config.mjs | 8 +- packages/email-templates/README.md | 2 +- .../src/emails/company-offer.email.tsx | 90 ++++++++++++++++++ packages/email-templates/src/index.ts | 1 + .../src/locales/et/company-offer-email.json | 8 ++ .../shared/src/schema/mailer.schema.ts | 2 +- public/locales/en/account.json | 3 +- public/locales/et/account.json | 9 +- public/locales/et/common.json | 6 ++ public/locales/ru/account.json | 3 +- tsconfig.json | 2 +- 18 files changed, 274 insertions(+), 87 deletions(-) create mode 100644 lib/services/mailer.service.ts create mode 100644 lib/validations/email.schema.ts create mode 100644 packages/email-templates/src/emails/company-offer.email.tsx create mode 100644 packages/email-templates/src/locales/et/company-offer-email.json diff --git a/app/(public)/register-company/page.tsx b/app/(public)/register-company/page.tsx index 905e0e6..faad2fc 100644 --- a/app/(public)/register-company/page.tsx +++ b/app/(public)/register-company/page.tsx @@ -1,18 +1,23 @@ -"use client"; +'use client'; -import { MedReportTitle } from "@/components/med-report-title"; -import React from "react"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { useForm } from "react-hook-form"; -import { companySchema } from "@/lib/validations/companySchema"; -import { CompanySubmitData } from "@/lib/types/company"; -import { submitCompanyRegistration } from "@/lib/services/register-company.service"; -import { useRouter } from "next/navigation"; -import { Label } from "@kit/ui/label"; -import { Input } from "@kit/ui/input"; -import { SubmitButton } from "@/components/ui/submit-button"; -import { FormItem } from "@kit/ui/form"; -import { Trans } from "@kit/ui/trans"; +import React from 'react'; + +import { useRouter } from 'next/navigation'; + +import { MedReportTitle } from '@/components/med-report-title'; +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 { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { FormItem } from '@kit/ui/form'; +import { Input } from '@kit/ui/input'; +import { Label } from '@kit/ui/label'; +import { Trans } from '@kit/ui/trans'; export default function RegisterCompany() { const router = useRouter(); @@ -21,9 +26,10 @@ export default function RegisterCompany() { handleSubmit, formState: { errors, isValid, isSubmitting }, } = useForm({ - resolver: yupResolver(companySchema), - mode: "onChange", + resolver: zodResolver(companySchema), + mode: 'onChange', }); + const language = useTranslation().i18n.language; async function onSubmit(data: CompanySubmitData) { const formData = new FormData(); @@ -33,57 +39,66 @@ export default function RegisterCompany() { try { await submitCompanyRegistration(formData); - router.push("/register-company/success"); + sendCompanyOfferEmail(data, language) + .then(() => router.push('/register-company/success')) + .catch((error) => alert('error: ' + error)); } catch (err: unknown) { if (err instanceof Error) { - alert("Server validation error: " + err.message); + alert('Server validation error: ' + err.message); } - alert("Server validation error"); + alert('Server validation error'); } } return ( -
-
+
+
-

Ettevõtte andmed

-

- Pakkumise saamiseks palun sisesta ettevõtte andmed millega MedReport - kasutada kavatsed. +

+ +

+

+

- - + + - - + + - - + + - - + + - +
-
-
+
); } diff --git a/app/(public)/register-company/success/page.tsx b/app/(public)/register-company/success/page.tsx index 9682cff..def24a5 100644 --- a/app/(public)/register-company/success/page.tsx +++ b/app/(public)/register-company/success/page.tsx @@ -1,10 +1,14 @@ -import { MedReportTitle } from "@/components/med-report-title"; -import Image from "next/image"; -import Link from "next/link"; +import Image from 'next/image'; +import Link from 'next/link'; + +import { MedReportTitle } from '@/components/med-report-title'; +import { Button } from '@/packages/ui/src/shadcn/button'; + +import { Trans } from '@kit/ui/trans'; export default function CompanyRegistrationSuccess() { return ( -
+
-

Päring edukalt saadetud!

-

Saadame teile esimesel võimalusel vastuse

+

+ +

+

+ +

-
); diff --git a/lib/dev-mock-modules.ts b/lib/dev-mock-modules.ts index b433cce..5861368 100644 --- a/lib/dev-mock-modules.ts +++ b/lib/dev-mock-modules.ts @@ -32,4 +32,4 @@ export const setUser = noop('Sentry.setUser'); export const loadStripe = noop('Stripe.loadStripe'); // Nodemailer -export const createTransport = noop('Nodemailer.createTransport'); +// export const createTransport = noop('Nodemailer.createTransport'); diff --git a/lib/services/mailer.service.ts b/lib/services/mailer.service.ts new file mode 100644 index 0000000..515bfcc --- /dev/null +++ b/lib/services/mailer.service.ts @@ -0,0 +1,42 @@ +'use server'; + +import { getMailer } from '@kit/mailers'; +import { enhanceAction } from '@kit/next/actions'; + +import { CompanySubmitData } from '../types/company'; +import { emailSchema } from '../validations/email.schema'; + +export const sendCompanyOfferEmail = async ( + data: CompanySubmitData, + language: string, +) => { + const { renderCompanyOfferEmail } = await import('@kit/email-templates'); + const { html, subject, to } = await renderCompanyOfferEmail({ + language, + companyData: data, + }); + + await sendEmail({ + subject, + html, + to, + }); +}; + +export const sendEmail = enhanceAction( + async ({ subject, html, to }) => { + const mailer = await getMailer(); + + await mailer.sendEmail({ + to, + subject, + html, + }); + + return {}; + }, + { + schema: emailSchema, + auth: false, + }, +); diff --git a/lib/services/register-company.service.ts b/lib/services/register-company.service.ts index c030462..7b973bd 100644 --- a/lib/services/register-company.service.ts +++ b/lib/services/register-company.service.ts @@ -1,31 +1,26 @@ -"use server"; +'use server'; -import * as yup from "yup"; -import { companySchema } from "@/lib/validations/companySchema"; +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() || "", + companyName: formData.get('companyName')?.toString() || '', + contactPerson: formData.get('contactPerson')?.toString() || '', + email: formData.get('email')?.toString() || '', + phone: formData.get('phone')?.toString() || '', }; - try { - await companySchema.validate(data, { abortEarly: false }); + const result = companySchema.safeParse(data); - console.log("Valid data:", data); - } catch (validationError) { - if (validationError instanceof yup.ValidationError) { - const errors = validationError.inner.map((err) => ({ - path: err.path, - message: err.message, - })); - throw new Error( - "Validation failed: " + - errors.map((e) => `${e.path}: ${e.message}`).join(", ") - ); - } - throw validationError; + 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/companySchema.ts index b265f81..aa35ebc 100644 --- a/lib/validations/companySchema.ts +++ b/lib/validations/companySchema.ts @@ -1,8 +1,16 @@ -import * as yup from "yup"; +import { z } from 'zod'; -export const companySchema = yup.object({ - companyName: yup.string().required("Company name is required"), - contactPerson: yup.string().required("Contact person is required"), - email: yup.string().email("Invalid email").required("Email is required"), - phone: yup.string().optional(), +export const companySchema = z.object({ + companyName: z.string({ + required_error: 'Company name is required', + }), + contactPerson: z.string({ + required_error: 'Contact person is required', + }), + email: z + .string({ + required_error: 'Email is required', + }) + .email('Invalid email'), + phone: z.string().optional(), }); diff --git a/lib/validations/email.schema.ts b/lib/validations/email.schema.ts new file mode 100644 index 0000000..58cc00d --- /dev/null +++ b/lib/validations/email.schema.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +export const emailSchema = z.object({ + to: z.string().email(), + subject: z.string().min(1).max(200), + html: z.string().min(1).max(5000), +}); diff --git a/next.config.mjs b/next.config.mjs index 0ffa657..73c7188 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -129,7 +129,7 @@ function getModulesAliases() { const excludeSentry = monitoringProvider !== 'sentry'; const excludeBaselime = monitoringProvider !== 'baselime'; const excludeStripe = billingProvider !== 'stripe'; - const excludeNodemailer = mailerProvider !== 'nodemailer'; + // const excludeNodemailer = mailerProvider !== 'nodemailer'; const excludeTurnstile = !captchaProvider; /** @type {Record} */ @@ -151,9 +151,9 @@ function getModulesAliases() { aliases['@stripe/stripe-js'] = noopPath; } - if (excludeNodemailer) { - aliases['nodemailer'] = noopPath; - } + // if (excludeNodemailer) { + // aliases['nodemailer'] = noopPath; + // } if (excludeTurnstile) { aliases['@marsidev/react-turnstile'] = noopPath; diff --git a/packages/email-templates/README.md b/packages/email-templates/README.md index dbd6ada..83ec48a 100644 --- a/packages/email-templates/README.md +++ b/packages/email-templates/README.md @@ -2,4 +2,4 @@ This package is responsible for managing email templates using the react.email library. -Here you can define email templates using React components and export them as a function that returns the email content. \ No newline at end of file +Here you can define email templates using React components and export them as a function that returns the email content. diff --git a/packages/email-templates/src/emails/company-offer.email.tsx b/packages/email-templates/src/emails/company-offer.email.tsx new file mode 100644 index 0000000..fa9f30f --- /dev/null +++ b/packages/email-templates/src/emails/company-offer.email.tsx @@ -0,0 +1,90 @@ +import { + Body, + Head, + Html, + Preview, + Tailwind, + Text, + render, +} from '@react-email/components'; + +import { BodyStyle } from '../components/body-style'; +import { EmailContent } from '../components/content'; +import { EmailHeader } from '../components/header'; +import { EmailHeading } from '../components/heading'; +import { EmailWrapper } from '../components/wrapper'; +import { initializeEmailI18n } from '../lib/i18n'; + +export async function renderCompanyOfferEmail({ + language, + companyData, +}: { + language?: string; + companyData: { + companyName: string; + contactPerson: string; + email: string; + phone?: string; + }; +}) { + const namespace = 'company-offer-email'; + + const { t } = await initializeEmailI18n({ + language, + namespace, + }); + + const to = process.env.CONTACT_EMAIL || ''; + + const previewText = t(`${namespace}:previewText`, { + companyName: companyData.companyName, + }); + + const subject = t(`${namespace}:subject`, { + companyName: companyData.companyName, + }); + + const html = await render( + + + + + + {previewText} + + + + + + {previewText} + + + + + {t(`${namespace}:companyName`)} {companyData.companyName} + + + + {t(`${namespace}:contactPerson`)} {companyData.contactPerson} + + + + {t(`${namespace}:email`)} {companyData.email} + + + + {t(`${namespace}:phone`)} {companyData.phone || 'N/A'} + + + + + + , + ); + + return { + html, + subject, + to, + }; +} diff --git a/packages/email-templates/src/index.ts b/packages/email-templates/src/index.ts index c7c2eb4..80e5f8a 100644 --- a/packages/email-templates/src/index.ts +++ b/packages/email-templates/src/index.ts @@ -1,3 +1,4 @@ export * from './emails/invite.email'; export * from './emails/account-delete.email'; export * from './emails/otp.email'; +export * from './emails/company-offer.email'; diff --git a/packages/email-templates/src/locales/et/company-offer-email.json b/packages/email-templates/src/locales/et/company-offer-email.json new file mode 100644 index 0000000..3a39792 --- /dev/null +++ b/packages/email-templates/src/locales/et/company-offer-email.json @@ -0,0 +1,8 @@ +{ + "subject": "Uus ettevõtte liitumispäring", + "previewText": "Ettevõte {{companyName}} soovib pakkumist", + "companyName": "Ettevõtte nimi:", + "contactPerson": "Kontaktisik:", + "email": "E-mail:", + "phone": "Telefon:" +} diff --git a/packages/mailers/shared/src/schema/mailer.schema.ts b/packages/mailers/shared/src/schema/mailer.schema.ts index 1fd1f58..dd0e652 100644 --- a/packages/mailers/shared/src/schema/mailer.schema.ts +++ b/packages/mailers/shared/src/schema/mailer.schema.ts @@ -5,7 +5,7 @@ export const MailerSchema = z to: z.string().email(), // this is not necessarily formatted // as an email so we type it loosely - from: z.string().min(1), + from: z.string().min(1).optional(), subject: z.string(), }) .and( diff --git a/public/locales/en/account.json b/public/locales/en/account.json index 28ee8d9..1b0924e 100644 --- a/public/locales/en/account.json +++ b/public/locales/en/account.json @@ -112,6 +112,5 @@ "noTeamsYet": "You don't have any teams yet.", "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", - "createCompanyAccount": "Create Company Account", - "requestCompanyAccount": "Request Company Account" + "createCompanyAccount": "Create Company Account" } diff --git a/public/locales/et/account.json b/public/locales/et/account.json index 3526791..a6af3a3 100644 --- a/public/locales/et/account.json +++ b/public/locales/et/account.json @@ -113,5 +113,12 @@ "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", "createCompanyAccount": "Create Company Account", - "requestCompanyAccount": "Küsi pakkumist" + "requestCompanyAccount": { + "title": "Ettevõtte andmed", + "description": "Pakkumise saamiseks palun sisesta ettevõtte andmed millega MedReport kasutada kavatsed.", + "button": "Küsi pakkumist", + "successTitle": "Päring edukalt saadetud!", + "successDescription": "Saadame teile esimesel võimalusel vastuse", + "successButton": "Tagasi kodulehele" + } } diff --git a/public/locales/et/common.json b/public/locales/et/common.json index c194ce3..076489f 100644 --- a/public/locales/et/common.json +++ b/public/locales/et/common.json @@ -92,5 +92,11 @@ "description": "This website uses cookies to ensure you get the best experience on our website.", "reject": "Reject", "accept": "Accept" + }, + "formField": { + "companyName": "Ettevõtte nimi", + "contactPerson": "Kontaktisik", + "email": "E-mail", + "phone": "Telefon" } } diff --git a/public/locales/ru/account.json b/public/locales/ru/account.json index 28ee8d9..1b0924e 100644 --- a/public/locales/ru/account.json +++ b/public/locales/ru/account.json @@ -112,6 +112,5 @@ "noTeamsYet": "You don't have any teams yet.", "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", - "createCompanyAccount": "Create Company Account", - "requestCompanyAccount": "Request Company Account" + "createCompanyAccount": "Create Company Account" } diff --git a/tsconfig.json b/tsconfig.json index 87b31f1..f3c8d06 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*":["./*"], + "@/*": ["./*"], "~/*": ["./app/*"], "~/config/*": ["./config/*"], "~/components/*": ["./components/*"], From 15798fdfdf174404a78502736b0cdaa525f5853f Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Tue, 17 Jun 2025 14:23:59 +0300 Subject: [PATCH 2/5] refactor: update register company page and success message; improve imports and localization --- app/(public)/register-company/page.tsx | 29 ++++++++++--------- .../register-company/success/page.tsx | 11 ++++--- public/locales/en/account.json | 5 +++- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/(public)/register-company/page.tsx b/app/(public)/register-company/page.tsx index 7e3875a..881a0fc 100644 --- a/app/(public)/register-company/page.tsx +++ b/app/(public)/register-company/page.tsx @@ -4,19 +4,20 @@ import React from 'react'; import { useRouter } from 'next/navigation'; -import { MedReportLogo } from "@/components/med-report-title"; -import React from "react"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { useForm } from "react-hook-form"; -import { companySchema } from "@/lib/validations/companySchema"; -import { CompanySubmitData } from "@/lib/types/company"; -import { submitCompanyRegistration } from "@/lib/services/register-company.service"; -import { useRouter } from "next/navigation"; -import { Label } from "@kit/ui/label"; -import { Input } from "@kit/ui/input"; -import { SubmitButton } from "@/components/ui/submit-button"; -import { FormItem } from "@kit/ui/form"; -import { Trans } from "@kit/ui/trans"; +import { MedReportLogo } from '@/components/med-report-title'; +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 { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { FormItem } from '@kit/ui/form'; +import { Input } from '@kit/ui/input'; +import { Label } from '@kit/ui/label'; +import { Trans } from '@kit/ui/trans'; export default function RegisterCompany() { const router = useRouter(); @@ -52,7 +53,7 @@ export default function RegisterCompany() { return (
- +

diff --git a/app/(public)/register-company/success/page.tsx b/app/(public)/register-company/success/page.tsx index 35f19e5..42f2634 100644 --- a/app/(public)/register-company/success/page.tsx +++ b/app/(public)/register-company/success/page.tsx @@ -1,14 +1,17 @@ +'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 (
- +

- +

- +

diff --git a/public/locales/en/account.json b/public/locales/en/account.json index 1b0924e..dbaa182 100644 --- a/public/locales/en/account.json +++ b/public/locales/en/account.json @@ -112,5 +112,8 @@ "noTeamsYet": "You don't have any teams yet.", "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", - "createCompanyAccount": "Create Company Account" + "createCompanyAccount": "Create Company Account", + "requestCompanyAccount": { + "title": "Company details" + } } From 6aa3a27d4465e9d49cd439df0e1f710a4e494654 Mon Sep 17 00:00:00 2001 From: Danel Kungla Date: Thu, 26 Jun 2025 16:05:37 +0300 Subject: [PATCH 3/5] feat: Implement company offer submission page and success notification - Added CompanyOffer component for submitting company offers with validation. - Integrated email sending functionality upon form submission. - Created a success page for company registration confirmation. - Introduced a reusable SuccessNotification component for displaying success messages. - Updated account update functionality with new fields and validation. - Enhanced user experience with back button and logo components. - Added necessary database migrations for account updates. --- README.md | 52 +- app/(marketing)/page.tsx | 4 +- .../page.tsx | 22 +- app/(public)/company-offer/success/page.tsx | 16 + .../register-company/success/page.tsx | 37 - app/(public)/sign-in/page.tsx | 8 +- app/auth/{ => callback}/layout.tsx | 0 app/auth/confirm/layout.tsx | 9 + app/auth/password-reset/layout.tsx | 9 + app/auth/sign-in/layout.tsx | 9 + app/auth/sign-in/page.tsx | 6 +- app/auth/sign-up/layout.tsx | 9 + app/auth/sign-up/page.tsx | 1 + app/auth/update-account/layout.tsx | 11 + app/auth/update-account/page.tsx | 43 + app/auth/update-account/success/page.tsx | 17 + app/auth/verify/layout.tsx | 9 + .../_lib/server/user-billing.service.ts | 2 +- .../_lib/server/team-billing.service.ts | 2 +- .../_lib/server/members-page.loader.ts | 3 +- components/app-logo.tsx | 2 +- components/back-button.tsx | 31 + components/header-auth.tsx | 32 +- ...d-report-title.tsx => med-report-logo.tsx} | 0 config/paths.config.ts | 4 + lib/actions/sign-out.tsx | 9 +- lib/database.types.ts | 1442 --------- lib/services/register-company.service.ts | 26 - ...mpanySchema.ts => company-offer.schema.ts} | 2 +- middleware.ts | 21 +- package.json | 6 +- .../src/hooks/use-personal-account-data.ts | 3 +- .../src/components/admin-account-page.tsx | 1 - .../components/sign-in-methods-container.tsx | 30 +- .../components/sign-up-methods-container.tsx | 1 + .../src/components/update-account-form.tsx | 225 ++ .../auth/src/schemas/update-account.schema.ts | 44 + .../server/actions/update-account-actions.ts | 44 + packages/features/auth/src/server/api.ts | 93 + packages/features/auth/src/sign-up.ts | 1 + packages/features/auth/tsconfig.json | 2 +- .../notifications/src/components/index.ts | 2 + .../src/components/success-notification.tsx | 50 + .../update-account-success-notification.tsx | 39 + packages/features/notifications/tsconfig.json | 2 +- packages/supabase/src/database.types.ts | 2565 +++++++++-------- packages/ui/src/makerkit/version-updater.tsx | 2 +- packages/ui/src/shadcn/checkbox.tsx | 2 +- packages/ui/tsconfig.json | 4 +- public/assets/arrow-left.tsx | 28 + public/assets/external-link.tsx | 16 + public/locales/et/account.json | 10 + public/locales/et/common.json | 11 +- supabase/database.types.ts | 1427 --------- .../20250620124230_update_account.sql | 119 + 55 files changed, 2340 insertions(+), 4225 deletions(-) rename app/(public)/{register-company => company-offer}/page.tsx (82%) create mode 100644 app/(public)/company-offer/success/page.tsx delete mode 100644 app/(public)/register-company/success/page.tsx rename app/auth/{ => callback}/layout.tsx (100%) create mode 100644 app/auth/confirm/layout.tsx create mode 100644 app/auth/password-reset/layout.tsx create mode 100644 app/auth/sign-in/layout.tsx create mode 100644 app/auth/sign-up/layout.tsx create mode 100644 app/auth/update-account/layout.tsx create mode 100644 app/auth/update-account/page.tsx create mode 100644 app/auth/update-account/success/page.tsx create mode 100644 app/auth/verify/layout.tsx create mode 100644 components/back-button.tsx rename components/{med-report-title.tsx => med-report-logo.tsx} (100%) delete mode 100644 lib/database.types.ts delete mode 100644 lib/services/register-company.service.ts rename lib/validations/{companySchema.ts => company-offer.schema.ts} (88%) create mode 100644 packages/features/auth/src/components/update-account-form.tsx create mode 100644 packages/features/auth/src/schemas/update-account.schema.ts create mode 100644 packages/features/auth/src/server/actions/update-account-actions.ts create mode 100644 packages/features/auth/src/server/api.ts create mode 100644 packages/features/notifications/src/components/success-notification.tsx create mode 100644 packages/features/notifications/src/components/update-account-success-notification.tsx create mode 100644 public/assets/arrow-left.tsx create mode 100644 public/assets/external-link.tsx delete mode 100644 supabase/database.types.ts create mode 100644 supabase/migrations/20250620124230_update_account.sql 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/database.types.ts b/lib/database.types.ts deleted file mode 100644 index 452ae7b..0000000 --- a/lib/database.types.ts +++ /dev/null @@ -1,1442 +0,0 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[]; - -export type Database = { - graphql_public: { - Tables: { - [_ in never]: never; - }; - Views: { - [_ in never]: never; - }; - Functions: { - graphql: { - Args: { - operationName?: string; - query?: string; - variables?: Json; - extensions?: Json; - }; - Returns: Json; - }; - }; - Enums: { - [_ in never]: never; - }; - CompositeTypes: { - [_ in never]: never; - }; - }; - public: { - Tables: { - 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; - }; - 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; - }; - 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: []; - }; - 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; - }; - Insert: { - 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; - }; - 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: '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_role_fkey'; - columns: ['account_role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; - }, - ]; - }; - billing_customers: { - Row: { - 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']; - }; - Update: { - 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: 'user_account_workspace'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; - }, - ]; - }; - config: { - Row: { - 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; - }; - Update: { - 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; - }; - 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; - }; - 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; - }; - 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: 'user_account_workspace'; - 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']; - }, - ]; - }; - 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; - }; - 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; - }; - 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: []; - }; - 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']; - }; - 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']; - }; - 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']; - }; - 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: 'user_account_workspace'; - 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; - }; - Insert: { - 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; - }; - Relationships: [ - { - 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; - }; - 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; - }; - 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; - }; - 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: 'user_account_workspace'; - 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']; - }, - ]; - }; - role_permissions: { - Row: { - id: number; - permission: Database['public']['Enums']['app_permissions']; - role: string; - }; - Insert: { - id?: number; - permission: Database['public']['Enums']['app_permissions']; - role: string; - }; - Update: { - id?: number; - permission?: Database['public']['Enums']['app_permissions']; - role?: string; - }; - Relationships: [ - { - foreignKeyName: 'role_permissions_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; - }, - ]; - }; - roles: { - Row: { - hierarchy_level: number; - name: string; - }; - Insert: { - hierarchy_level: number; - name: string; - }; - Update: { - 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; - }; - 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; - }; - 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; - }; - Relationships: [ - { - 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; - }; - 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; - }; - 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; - }; - 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: 'user_account_workspace'; - 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']; - }, - ]; - }; - }; - Views: { - user_account_workspace: { - Row: { - id: string | null; - name: string | null; - picture_url: string | null; - subscription_status: - | 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; - }; - Relationships: [ - { - 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; - }; - add_invitations_to_account: { - Args: { - 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; - }; - create_invitation: { - 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; - }; - }; - 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; - }; - create_team_account: { - 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; - }; - }; - get_account_invitations: { - 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; - }[]; - }; - get_account_members: { - 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; - }[]; - }; - get_config: { - Args: Record; - Returns: Json; - }; - get_nonce_status: { - Args: { - p_id: string; - }; - Returns: Json; - }; - get_upper_system_role: { - Args: Record; - Returns: string; - }; - has_active_subscription: { - 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; - }; - has_permission: { - Args: { - user_id: string; - account_id: string; - permission_name: Database['public']['Enums']['app_permissions']; - }; - Returns: boolean; - }; - has_role_on_account: { - 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; - }; - is_aal2: { - Args: Record; - Returns: boolean; - }; - is_account_owner: { - Args: { - account_id: string; - }; - Returns: boolean; - }; - is_account_team_member: { - Args: { - target_account_id: string; - }; - Returns: boolean; - }; - is_mfa_compliant: { - Args: Record; - Returns: boolean; - }; - is_set: { - Args: { - field_name: string; - }; - Returns: boolean; - }; - is_super_admin: { - Args: Record; - Returns: boolean; - }; - is_team_member: { - Args: { - account_id: string; - user_id: string; - }; - Returns: boolean; - }; - revoke_nonce: { - Args: { - p_id: string; - p_reason?: string; - }; - Returns: boolean; - }; - team_account_workspace: { - 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'][]; - }[]; - }; - transfer_team_account_ownership: { - Args: { - target_account_id: string; - new_owner_id: 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; - }; - 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; - }; - }; - 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; - }; - 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; - }; - }; - 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; - }; - }; - Enums: { - 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'; - }; - 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; - }; - }; -}; - -type PublicSchema = Database[Extract]; - -export type Tables< - PublicTableNameOrOptions extends - | keyof (PublicSchema['Tables'] & PublicSchema['Views']) - | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views']) - : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['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; - } - ? R - : never - : never; - -export type TablesInsert< - PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] - | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] - : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { - Insert: infer I; - } - ? I - : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { - Insert: infer I; - } - ? I - : never - : never; - -export type TablesUpdate< - PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] - | { schema: keyof Database }, - TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] - : never = never, -> = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { - Update: infer U; - } - ? U - : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { - Update: infer U; - } - ? U - : never - : never; - -export type Enums< - PublicEnumNameOrOptions extends - | keyof PublicSchema['Enums'] - | { schema: keyof Database }, - EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums'] - : never = never, -> = PublicEnumNameOrOptions extends { schema: keyof Database } - ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName] - : PublicEnumNameOrOptions extends keyof PublicSchema['Enums'] - ? PublicSchema['Enums'][PublicEnumNameOrOptions] - : never; - -export type CompositeTypes< - PublicCompositeTypeNameOrOptions extends - | keyof PublicSchema['CompositeTypes'] - | { schema: keyof Database }, - CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof Database; - } - ? 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; 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..726c9e5 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,22 +147,23 @@ 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; + // TODO: this mfa should only be applied to SUPER_ADMIN + // const isVerifyMfa = req.nextUrl.pathname === pathsConfig.auth.verifyMfa; // 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 (!isVerifyMfa) { + // const nextPath = + // req.nextUrl.searchParams.get('next') ?? pathsConfig.app.home; - return NextResponse.redirect( - new URL(nextPath, req.nextUrl.origin).href, - ); - } + // return NextResponse.redirect( + // new URL(nextPath, req.nextUrl.origin).href, + // ); + // } }, }, { diff --git a/package.json b/package.json index be13199..0be996a 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 > ./lib/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-data:dev": "NODE_ENV=local ts-node jobs/sync-analysis-groups.ts" }, @@ -113,4 +111,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); }} > - + + ), + cardVariant: 'gradient-success' as CardProps['variant'], + }, +]; + +const dummyRecommendations = [ + { + icon: , + color: 'bg-cyan/10 text-cyan', + title: 'Kolesterooli kontroll', + description: 'HDL-kolestrool', + tooltipContent: 'Selgitus', + price: '20,00 €', + buttonText: 'Telli', + }, + { + icon: , + color: 'bg-primary/10 text-primary', + title: 'Kolesterooli kontroll', + tooltipContent: 'Selgitus', + description: 'LDL-Kolesterool', + buttonText: 'Broneeri', + }, + { + icon: , + color: 'bg-destructive/10 text-destructive', + title: 'Vererõhu kontroll', + tooltipContent: 'Selgitus', + description: 'Score-Risk 2', + price: '20,00 €', + buttonText: 'Telli', + }, +]; + +export default function Dashboard() { + const userWorkspace = useUserWorkspace(); + const account = usePersonalAccountData(userWorkspace.user.id); + + return ( + <> +
+

+ + {account?.data?.name ? `, ${toTitleCase(account.data.name)}` : ''} +

+ + : + +
+
+ {dummyCards.map( + ({ + title, + description, + icon, + iconBg, + cardVariant, + descriptionColor, + }) => ( + + +
+ {icon} +
+
+ +
+ +
+ + + +
+
+ ), + )} +
+ + +

+ +

+
+ + {dummyRecommendations.map( + ( + { + icon, + color, + title, + description, + tooltipContent, + price, + buttonText, + }, + index, + ) => { + return ( +
+
+
+ {icon} +
+
+
+ {title} + +
+

+ {description} +

+
+
+
+

{price}

+ +
+
+ ); + }, + )} +
+
+ + ); +} diff --git a/app/home/(user)/_components/home-menu-navigation.tsx b/app/home/(user)/_components/home-menu-navigation.tsx index d91e498..6f2db1a 100644 --- a/app/home/(user)/_components/home-menu-navigation.tsx +++ b/app/home/(user)/_components/home-menu-navigation.tsx @@ -1,65 +1,43 @@ -import { - BorderedNavigationMenu, - BorderedNavigationMenuItem, -} from '@kit/ui/bordered-navigation-menu'; -import { If } from '@kit/ui/if'; +import { ShoppingCart } from 'lucide-react'; + +import { Button } from '@kit/ui/button'; +import { Trans } from '@kit/ui/trans'; +import { cn } from '@kit/ui/utils'; import { AppLogo } from '~/components/app-logo'; import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container'; -import featuresFlagConfig from '~/config/feature-flags.config'; -import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config'; +import { Search } from '~/components/ui/search'; +import { SIDEBAR_WIDTH } from '../../../../packages/ui/src/shadcn/constants'; // home imports -import { HomeAccountSelector } from '../_components/home-account-selector'; import { UserNotifications } from '../_components/user-notifications'; import { type UserWorkspace } from '../_lib/server/load-user-workspace'; export function HomeMenuNavigation(props: { workspace: UserWorkspace }) { - const { workspace, user, accounts } = props.workspace; - - const routes = personalAccountNavigationConfig.routes.reduce< - Array<{ - path: string; - label: string; - Icon?: React.ReactNode; - end?: boolean | ((path: string) => boolean); - }> - >((acc, item) => { - if ('children' in item) { - return [...acc, ...item.children]; - } - - if ('divider' in item) { - return acc; - } - - return [...acc, item]; - }, []); + const { workspace, user } = props.workspace; return ( -
-
+
+
- - - {routes.map((route) => ( - - ))} -
+ } + /> -
+
+ - - - -
diff --git a/app/home/(user)/_components/home-sidebar.tsx b/app/home/(user)/_components/home-sidebar.tsx index e434119..e1e53b6 100644 --- a/app/home/(user)/_components/home-sidebar.tsx +++ b/app/home/(user)/_components/home-sidebar.tsx @@ -1,61 +1,29 @@ -import { If } from '@kit/ui/if'; import { Sidebar, SidebarContent, - SidebarFooter, SidebarHeader, SidebarNavigation, } from '@kit/ui/shadcn-sidebar'; -import { cn } from '@kit/ui/utils'; +import { Trans } from '@kit/ui/trans'; -import { AppLogo } from '~/components/app-logo'; -import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container'; -import featuresFlagConfig from '~/config/feature-flags.config'; import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config'; -import { UserNotifications } from '~/home/(user)/_components/user-notifications'; -// home imports -import type { UserWorkspace } from '../_lib/server/load-user-workspace'; -import { HomeAccountSelector } from './home-account-selector'; - -interface HomeSidebarProps { - workspace: UserWorkspace; -} - -export function HomeSidebar(props: HomeSidebarProps) { - const { workspace, user, accounts } = props.workspace; +export function HomeSidebar() { const collapsible = personalAccountNavigationConfig.sidebarCollapsedStyle; return ( - -
- - } - > - - - -
- -
+ +
+
+ +
- - - - ); } diff --git a/app/home/(user)/layout.tsx b/app/home/(user)/layout.tsx index 6ede447..97f38ae 100644 --- a/app/home/(user)/layout.tsx +++ b/app/home/(user)/layout.tsx @@ -39,7 +39,7 @@ function SidebarLayout({ children }: React.PropsWithChildren) { - + @@ -58,8 +58,8 @@ function HeaderLayout({ children }: React.PropsWithChildren) { return ( - - + + @@ -67,7 +67,14 @@ function HeaderLayout({ children }: React.PropsWithChildren) { - {children} + + + + + + {children} + + ); diff --git a/app/home/(user)/page.tsx b/app/home/(user)/page.tsx index 3327e1f..cc64e11 100644 --- a/app/home/(user)/page.tsx +++ b/app/home/(user)/page.tsx @@ -4,6 +4,7 @@ import { Trans } from '@kit/ui/trans'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +import Dashboard from './_components/dashboard'; // local imports import { HomeLayoutPageHeader } from './_components/home-page-header'; @@ -21,10 +22,12 @@ function UserHomePage() { <> } - description={} + description={<>} /> - + + + ); } diff --git a/components/ui/info-tooltip.tsx b/components/ui/info-tooltip.tsx new file mode 100644 index 0000000..7883844 --- /dev/null +++ b/components/ui/info-tooltip.tsx @@ -0,0 +1,16 @@ +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@kit/ui/tooltip"; +import { Info } from "lucide-react"; + +export function InfoTooltip({ content }: { content?: string }) { + if (!content) return null; + return ( + + + + + + {content} + + + ); +} diff --git a/components/ui/search.tsx b/components/ui/search.tsx new file mode 100644 index 0000000..84dac99 --- /dev/null +++ b/components/ui/search.tsx @@ -0,0 +1,33 @@ +import React, { JSX, ReactNode } from 'react'; + +import { cn } from '@kit/ui/utils'; + +export type SearchProps = React.InputHTMLAttributes & { + startElement?: string | JSX.Element; + className?: string; +}; + +const Search = React.forwardRef( + ({ className, startElement, ...props }, ref) => { + return ( +
+ {!!startElement && startElement} + +
+ ); + }, +); + +Search.displayName = 'Search'; + +export { Search }; diff --git a/config/paths.config.ts b/config/paths.config.ts index 340e208..f4cce84 100644 --- a/config/paths.config.ts +++ b/config/paths.config.ts @@ -13,6 +13,12 @@ const PathsSchema = z.object({ }), app: z.object({ home: z.string().min(1), + booking: z.string().min(1), + myOrders: z.string().min(1), + analysisResults: z.string().min(1), + orderAnalysisPackage: z.string().min(1), + orderAnalysis: z.string().min(1), + orderHealthAnalysis: z.string().min(1), personalAccountSettings: z.string().min(1), personalAccountBilling: z.string().min(1), personalAccountBillingReturn: z.string().min(1), @@ -47,6 +53,13 @@ const pathsConfig = PathsSchema.parse({ accountMembers: `/home/[account]/members`, accountBillingReturn: `/home/[account]/billing/return`, joinTeam: '/join', + // these routes are added as placeholders and can be changed when the pages are added + booking: '/booking', + myOrders: '/my-orders', + analysisResults: '/analysis-results', + orderAnalysisPackage: '/order-analysis-package', + orderAnalysis: '/order-analysis', + orderHealthAnalysis: '/order-health-analysis' }, } satisfies z.infer); diff --git a/config/personal-account-navigation.config.tsx b/config/personal-account-navigation.config.tsx index d534f32..f52c78e 100644 --- a/config/personal-account-navigation.config.tsx +++ b/config/personal-account-navigation.config.tsx @@ -1,47 +1,72 @@ -import { CreditCard, Home, User } from 'lucide-react'; +import { + FileLineChart, + HeartPulse, + LineChart, + MousePointerClick, + ShoppingCart, + Stethoscope, + TestTube2, +} from 'lucide-react'; import { z } from 'zod'; import { NavigationConfigSchema } from '@kit/ui/navigation-schema'; -import featureFlagsConfig from '~/config/feature-flags.config'; import pathsConfig from '~/config/paths.config'; -const iconClasses = 'w-4'; +const iconClasses = 'w-4 stroke-[1.5px]'; const routes = [ { - label: 'common:routes.application', children: [ { - label: 'common:routes.home', + label: 'common:routes.overview', path: pathsConfig.app.home, - Icon: , + Icon: , + end: true, + }, + { + label: 'common:routes.booking', + path: pathsConfig.app.booking, + Icon: , + end: true, + }, + { + label: 'common:routes.myOrders', + path: pathsConfig.app.myOrders, + Icon: , + end: true, + }, + { + label: 'common:routes.analysisResults', + path: pathsConfig.app.analysisResults, + Icon: , + end: true, + }, + { + label: 'common:routes.orderAnalysisPackage', + path: pathsConfig.app.orderAnalysisPackage, + Icon: , + end: true, + }, + { + label: 'common:routes.orderAnalysis', + path: pathsConfig.app.orderAnalysis, + Icon: , + end: true, + }, + { + label: 'common:routes.orderHealthAnalysis', + path: pathsConfig.app.orderHealthAnalysis, + Icon: , end: true, }, ], }, - { - label: 'common:routes.settings', - children: [ - { - label: 'common:routes.profile', - path: pathsConfig.app.personalAccountSettings, - Icon: , - }, - featureFlagsConfig.enablePersonalAccountBilling - ? { - label: 'common:routes.billing', - path: pathsConfig.app.personalAccountBilling, - Icon: , - } - : undefined, - ].filter((route) => !!route), - }, ] satisfies z.infer['routes']; export const personalAccountNavigationConfig = NavigationConfigSchema.parse({ routes, - style: process.env.NEXT_PUBLIC_USER_NAVIGATION_STYLE, - sidebarCollapsed: process.env.NEXT_PUBLIC_HOME_SIDEBAR_COLLAPSED, - sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSED_STYLE, + style: 'custom', + sidebarCollapsed: false, + sidebarCollapsedStyle: 'icon', }); diff --git a/lib/i18n/i18n.settings.ts b/lib/i18n/i18n.settings.ts index bb70925..78308fa 100644 --- a/lib/i18n/i18n.settings.ts +++ b/lib/i18n/i18n.settings.ts @@ -32,6 +32,7 @@ export const defaultI18nNamespaces = [ 'teams', 'billing', 'marketing', + 'dashboard', ]; /** diff --git a/lib/utils.ts b/lib/utils.ts index 405dd01..1eb8901 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,5 +1,5 @@ -import { clsx, type ClassValue } from "clsx"; -import { twMerge } from "tailwind-merge"; +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -9,3 +9,12 @@ export function toArray(input?: T | T[] | null): T[] { if (!input) return []; return Array.isArray(input) ? input : [input]; } + +export function toTitleCase(str?: string) { + if (!str) return ''; + return str.replace( + /\w\S*/g, + (text: string) => + text.charAt(0).toUpperCase() + text.substring(1).toLowerCase(), + ); +} diff --git a/packages/features/notifications/src/components/notifications-popover.tsx b/packages/features/notifications/src/components/notifications-popover.tsx index c2c0116..208351b 100644 --- a/packages/features/notifications/src/components/notifications-popover.tsx +++ b/packages/features/notifications/src/components/notifications-popover.tsx @@ -121,12 +121,12 @@ export function NotificationsPopover(params: { return ( -