feat: implement membership confirmation flow and update related functionalities
This commit is contained in:
@@ -70,3 +70,12 @@ To update database types run:
|
|||||||
```bash
|
```bash
|
||||||
npm run supabase:typegen:app
|
npm run supabase:typegen:app
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Super admin
|
||||||
|
|
||||||
|
To access admin pages follow these steps:
|
||||||
|
|
||||||
|
- Register new user
|
||||||
|
- Go to Profile and add Multi-Factor Authentication
|
||||||
|
- Sign out and Sign in
|
||||||
|
- Authenticate with mfa (at current time profile page prompts it again)
|
||||||
|
|||||||
11
app/auth/membership-confirmation/layout.tsx
Normal file
11
app/auth/membership-confirmation/layout.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
|
async function SiteLayout(props: React.PropsWithChildren) {
|
||||||
|
return (
|
||||||
|
<div className={'flex min-h-[100vh] flex-col items-center justify-center'}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withI18n(SiteLayout);
|
||||||
46
app/auth/membership-confirmation/page.tsx
Normal file
46
app/auth/membership-confirmation/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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 '@kit/notifications/components';
|
||||||
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||||
|
|
||||||
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
|
async function UpdateAccountSuccess() {
|
||||||
|
const { t } = useTranslation('account');
|
||||||
|
const client = getSupabaseServerClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await client.auth.getUser();
|
||||||
|
|
||||||
|
if (!user?.id) {
|
||||||
|
redirect(pathsConfig.app.home);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: accountData } = usePersonalAccountData(user.id);
|
||||||
|
|
||||||
|
if (!accountData?.id) {
|
||||||
|
redirect(pathsConfig.app.home);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SuccessNotification
|
||||||
|
showLogo={false}
|
||||||
|
title={t('account:membershipConfirmation:successTitle', {
|
||||||
|
firstName: accountData?.name,
|
||||||
|
lastName: accountData?.last_name,
|
||||||
|
})}
|
||||||
|
descriptionKey="account:membershipConfirmation:successDescription"
|
||||||
|
buttonProps={{
|
||||||
|
buttonTitleKey: 'account:membershipConfirmation:successButton',
|
||||||
|
href: pathsConfig.app.selectPackage,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withI18n(UpdateAccountSuccess);
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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 <UpdateAccountSuccessNotification userId={user?.id} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withI18n(UpdateAccountSuccess);
|
|
||||||
@@ -10,6 +10,7 @@ const PathsSchema = z.object({
|
|||||||
passwordUpdate: z.string().min(1),
|
passwordUpdate: z.string().min(1),
|
||||||
updateAccount: z.string().min(1),
|
updateAccount: z.string().min(1),
|
||||||
updateAccountSuccess: z.string().min(1),
|
updateAccountSuccess: z.string().min(1),
|
||||||
|
membershipConfirmation: z.string().min(1),
|
||||||
}),
|
}),
|
||||||
app: z.object({
|
app: z.object({
|
||||||
home: z.string().min(1),
|
home: z.string().min(1),
|
||||||
@@ -42,6 +43,7 @@ const pathsConfig = PathsSchema.parse({
|
|||||||
passwordUpdate: '/update-password',
|
passwordUpdate: '/update-password',
|
||||||
updateAccount: '/auth/update-account',
|
updateAccount: '/auth/update-account',
|
||||||
updateAccountSuccess: '/auth/update-account/success',
|
updateAccountSuccess: '/auth/update-account/success',
|
||||||
|
membershipConfirmation: '/auth/membership-confirmation',
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
home: '/home',
|
home: '/home',
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"react-hook-form": "^7.57.0",
|
"react-hook-form": "^7.57.0",
|
||||||
"supabase": "^2.26.9",
|
"supabase": "^2.30.4",
|
||||||
"tailwindcss": "4.1.7",
|
"tailwindcss": "4.1.7",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
|
|||||||
@@ -53,10 +53,7 @@ class AccountsApi {
|
|||||||
async loadUserAccounts() {
|
async loadUserAccounts() {
|
||||||
const authUser = await this.client.auth.getUser();
|
const authUser = await this.client.auth.getUser();
|
||||||
|
|
||||||
const {
|
const { data, error: userError } = authUser;
|
||||||
data,
|
|
||||||
error: userError,
|
|
||||||
} = authUser
|
|
||||||
|
|
||||||
if (userError) {
|
if (userError) {
|
||||||
throw userError;
|
throw userError;
|
||||||
@@ -66,14 +63,16 @@ class AccountsApi {
|
|||||||
|
|
||||||
const { data: accounts, error } = await this.client
|
const { data: accounts, error } = await this.client
|
||||||
.from('accounts_memberships')
|
.from('accounts_memberships')
|
||||||
.select(`
|
.select(
|
||||||
|
`
|
||||||
account_id,
|
account_id,
|
||||||
user_accounts (
|
user_accounts (
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
picture_url
|
picture_url,
|
||||||
|
)
|
||||||
|
`,
|
||||||
)
|
)
|
||||||
`)
|
|
||||||
.eq('user_id', user.id)
|
.eq('user_id', user.id)
|
||||||
.eq('account_role', 'owner');
|
.eq('account_role', 'owner');
|
||||||
|
|
||||||
@@ -88,7 +87,6 @@ class AccountsApi {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async loadTempUserAccounts() {
|
async loadTempUserAccounts() {
|
||||||
const { data: accounts, error } = await this.client
|
const { data: accounts, error } = await this.client
|
||||||
.from('user_accounts')
|
.from('user_accounts')
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
"./mfa": "./src/mfa.ts",
|
"./mfa": "./src/mfa.ts",
|
||||||
"./captcha/client": "./src/captcha/client/index.ts",
|
"./captcha/client": "./src/captcha/client/index.ts",
|
||||||
"./captcha/server": "./src/captcha/server/index.ts",
|
"./captcha/server": "./src/captcha/server/index.ts",
|
||||||
"./resend-email-link": "./src/components/resend-auth-link-form.tsx"
|
"./resend-email-link": "./src/components/resend-auth-link-form.tsx",
|
||||||
|
"./lib/utils/*": "./src/lib/utils/*.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.0.1",
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ export function SignInMethodsContainer(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data: hasPersonalCode } = await client.rpc(
|
const { data: hasConsentPersonalData } = await client.rpc(
|
||||||
'has_personal_code',
|
'has_consent_personal_data',
|
||||||
{
|
{
|
||||||
account_id: userId,
|
account_id: userId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasPersonalCode) {
|
if (hasConsentPersonalData) {
|
||||||
router.replace(props.paths.returnPath);
|
router.replace(props.paths.returnPath);
|
||||||
} else {
|
} else {
|
||||||
router.replace(props.paths.updateAccount);
|
router.replace(props.paths.updateAccount);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export interface AccountSubmitData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const onUpdateAccount = enhanceAction(
|
export const onUpdateAccount = enhanceAction(
|
||||||
async (params) => {
|
async (params: AccountSubmitData) => {
|
||||||
const client = getSupabaseServerClient();
|
const client = getSupabaseServerClient();
|
||||||
const api = createAuthApi(client);
|
const api = createAuthApi(client);
|
||||||
|
|
||||||
@@ -36,7 +36,14 @@ export const onUpdateAccount = enhanceAction(
|
|||||||
}
|
}
|
||||||
console.warn('On update account error: ', err);
|
console.warn('On update account error: ', err);
|
||||||
}
|
}
|
||||||
redirect(pathsConfig.auth.updateAccountSuccess);
|
const hasUnseenMembershipConfirmation =
|
||||||
|
await api.hasUnseenMembershipConfirmation();
|
||||||
|
|
||||||
|
if (hasUnseenMembershipConfirmation) {
|
||||||
|
redirect(pathsConfig.auth.membershipConfirmation);
|
||||||
|
} else {
|
||||||
|
redirect(pathsConfig.app.selectPackage);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
schema: UpdateAccountSchema,
|
schema: UpdateAccountSchema,
|
||||||
|
|||||||
@@ -13,14 +13,24 @@ class AuthApi {
|
|||||||
constructor(private readonly client: SupabaseClient<Database>) {}
|
constructor(private readonly client: SupabaseClient<Database>) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name hasPersonalCode
|
* @name hasUnseenMembershipConfirmation
|
||||||
* @description Check if given account ID has added personal code.
|
* @description Check if given user ID has any unseen membership confirmation.
|
||||||
* @param id
|
|
||||||
*/
|
*/
|
||||||
async hasPersonalCode(id: string) {
|
async hasUnseenMembershipConfirmation() {
|
||||||
const { data, error } = await this.client.rpc('has_personal_code', {
|
const {
|
||||||
account_id: id,
|
data: { user },
|
||||||
});
|
} = await this.client.auth.getUser();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await this.client.rpc(
|
||||||
|
'has_unseen_membership_confirmation',
|
||||||
|
{
|
||||||
|
p_user_id: user.id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './notifications-popover';
|
export * from './notifications-popover';
|
||||||
export * from './success-notification';
|
export * from './success-notification';
|
||||||
export * from './update-account-success-notification';
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
'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 (
|
|
||||||
<SuccessNotification
|
|
||||||
showLogo={false}
|
|
||||||
title={t('account:updateAccount:successTitle', {
|
|
||||||
firstName: accountData?.name,
|
|
||||||
lastName: accountData?.last_name,
|
|
||||||
})}
|
|
||||||
descriptionKey="account:updateAccount:successDescription"
|
|
||||||
buttonProps={{
|
|
||||||
buttonTitleKey: 'account:updateAccount:successButton',
|
|
||||||
href: pathsConfig.app.selectPackage,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -117,7 +117,10 @@ class AccountInvitationsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isUserAlreadyMember = members.find((member) => {
|
const isUserAlreadyMember = members.find((member) => {
|
||||||
return member.email === invitation.email || member.personal_code === invitation.personal_code;
|
return (
|
||||||
|
member.email === invitation.email ||
|
||||||
|
member.personal_code === invitation.personal_code
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isUserAlreadyMember) {
|
if (isUserAlreadyMember) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export type Database = {
|
|||||||
changed_data: Json | null
|
changed_data: Json | null
|
||||||
id: number
|
id: number
|
||||||
operation: string
|
operation: string
|
||||||
record_key: number | null
|
record_key: string | null
|
||||||
row_data: Json | null
|
row_data: Json | null
|
||||||
schema_name: string
|
schema_name: string
|
||||||
table_name: string
|
table_name: string
|
||||||
@@ -29,7 +29,7 @@ export type Database = {
|
|||||||
changed_data?: Json | null
|
changed_data?: Json | null
|
||||||
id?: number
|
id?: number
|
||||||
operation: string
|
operation: string
|
||||||
record_key?: number | null
|
record_key?: string | null
|
||||||
row_data?: Json | null
|
row_data?: Json | null
|
||||||
schema_name: string
|
schema_name: string
|
||||||
table_name: string
|
table_name: string
|
||||||
@@ -41,7 +41,7 @@ export type Database = {
|
|||||||
changed_data?: Json | null
|
changed_data?: Json | null
|
||||||
id?: number
|
id?: number
|
||||||
operation?: string
|
operation?: string
|
||||||
record_key?: number | null
|
record_key?: string | null
|
||||||
row_data?: Json | null
|
row_data?: Json | null
|
||||||
schema_name?: string
|
schema_name?: string
|
||||||
table_name?: string
|
table_name?: string
|
||||||
@@ -252,6 +252,7 @@ export type Database = {
|
|||||||
account_role: string
|
account_role: string
|
||||||
created_at: string
|
created_at: string
|
||||||
created_by: string | null
|
created_by: string | null
|
||||||
|
has_seen_confirmation: boolean
|
||||||
updated_at: string
|
updated_at: string
|
||||||
updated_by: string | null
|
updated_by: string | null
|
||||||
user_id: string
|
user_id: string
|
||||||
@@ -261,6 +262,7 @@ export type Database = {
|
|||||||
account_role: string
|
account_role: string
|
||||||
created_at?: string
|
created_at?: string
|
||||||
created_by?: string | null
|
created_by?: string | null
|
||||||
|
has_seen_confirmation?: boolean
|
||||||
updated_at?: string
|
updated_at?: string
|
||||||
updated_by?: string | null
|
updated_by?: string | null
|
||||||
user_id: string
|
user_id: string
|
||||||
@@ -270,6 +272,7 @@ export type Database = {
|
|||||||
account_role?: string
|
account_role?: string
|
||||||
created_at?: string
|
created_at?: string
|
||||||
created_by?: string | null
|
created_by?: string | null
|
||||||
|
has_seen_confirmation?: boolean
|
||||||
updated_at?: string
|
updated_at?: string
|
||||||
updated_by?: string | null
|
updated_by?: string | null
|
||||||
user_id?: string
|
user_id?: string
|
||||||
@@ -901,74 +904,21 @@ export type Database = {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
medreport_product_groups: {
|
medusa_products_analyses_relations: {
|
||||||
Row: {
|
|
||||||
created_at: string
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
updated_at: string | null
|
|
||||||
}
|
|
||||||
Insert: {
|
|
||||||
created_at?: string
|
|
||||||
id?: number
|
|
||||||
name: string
|
|
||||||
updated_at?: string | null
|
|
||||||
}
|
|
||||||
Update: {
|
|
||||||
created_at?: string
|
|
||||||
id?: number
|
|
||||||
name?: string
|
|
||||||
updated_at?: string | null
|
|
||||||
}
|
|
||||||
Relationships: []
|
|
||||||
}
|
|
||||||
medreport_products: {
|
|
||||||
Row: {
|
|
||||||
created_at: string
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
product_group_id: number | null
|
|
||||||
updated_at: string | null
|
|
||||||
}
|
|
||||||
Insert: {
|
|
||||||
created_at?: string
|
|
||||||
id?: number
|
|
||||||
name: string
|
|
||||||
product_group_id?: number | null
|
|
||||||
updated_at?: string | null
|
|
||||||
}
|
|
||||||
Update: {
|
|
||||||
created_at?: string
|
|
||||||
id?: number
|
|
||||||
name?: string
|
|
||||||
product_group_id?: number | null
|
|
||||||
updated_at?: string | null
|
|
||||||
}
|
|
||||||
Relationships: [
|
|
||||||
{
|
|
||||||
foreignKeyName: "medreport_products_product_groups_id_fkey"
|
|
||||||
columns: ["product_group_id"]
|
|
||||||
isOneToOne: false
|
|
||||||
referencedRelation: "medreport_product_groups"
|
|
||||||
referencedColumns: ["id"]
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
medreport_products_analyses_relations: {
|
|
||||||
Row: {
|
Row: {
|
||||||
analysis_element_id: number | null
|
analysis_element_id: number | null
|
||||||
analysis_id: number | null
|
analysis_id: number | null
|
||||||
product_id: number
|
medusa_product_id: number
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
analysis_element_id?: number | null
|
analysis_element_id?: number | null
|
||||||
analysis_id?: number | null
|
analysis_id?: number | null
|
||||||
product_id: number
|
medusa_product_id: number
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
analysis_element_id?: number | null
|
analysis_element_id?: number | null
|
||||||
analysis_id?: number | null
|
analysis_id?: number | null
|
||||||
product_id?: number
|
medusa_product_id?: number
|
||||||
}
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
@@ -985,27 +935,20 @@ export type Database = {
|
|||||||
referencedRelation: "analyses"
|
referencedRelation: "analyses"
|
||||||
referencedColumns: ["id"]
|
referencedColumns: ["id"]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
foreignKeyName: "medreport_products_analyses_product_id_fkey"
|
|
||||||
columns: ["product_id"]
|
|
||||||
isOneToOne: true
|
|
||||||
referencedRelation: "medreport_products"
|
|
||||||
referencedColumns: ["id"]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
medreport_products_external_services_relations: {
|
medusa_products_external_services_relations: {
|
||||||
Row: {
|
Row: {
|
||||||
connected_online_service_id: number
|
connected_online_service_id: number
|
||||||
product_id: number
|
medusa_product_id: number
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
connected_online_service_id: number
|
connected_online_service_id: number
|
||||||
product_id: number
|
medusa_product_id: number
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
connected_online_service_id?: number
|
connected_online_service_id?: number
|
||||||
product_id?: number
|
medusa_product_id?: number
|
||||||
}
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
@@ -1015,13 +958,6 @@ export type Database = {
|
|||||||
referencedRelation: "connected_online_services"
|
referencedRelation: "connected_online_services"
|
||||||
referencedColumns: ["id"]
|
referencedColumns: ["id"]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
foreignKeyName: "medreport_products_connected_online_services_product_id_fkey"
|
|
||||||
columns: ["product_id"]
|
|
||||||
isOneToOne: false
|
|
||||||
referencedRelation: "medreport_products"
|
|
||||||
referencedColumns: ["id"]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
nonces: {
|
nonces: {
|
||||||
@@ -1464,10 +1400,6 @@ export type Database = {
|
|||||||
Args: { target_team_account_id: string; target_user_id: string }
|
Args: { target_team_account_id: string; target_user_id: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
check_personal_code_exists: {
|
|
||||||
Args: { code: string }
|
|
||||||
Returns: boolean
|
|
||||||
}
|
|
||||||
create_invitation: {
|
create_invitation: {
|
||||||
Args: { account_id: string; email: string; role: string }
|
Args: { account_id: string; email: string; role: string }
|
||||||
Returns: {
|
Returns: {
|
||||||
@@ -1574,6 +1506,10 @@ export type Database = {
|
|||||||
Args: { target_account_id: string }
|
Args: { target_account_id: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
|
has_consent_personal_data: {
|
||||||
|
Args: { account_id: string }
|
||||||
|
Returns: boolean
|
||||||
|
}
|
||||||
has_more_elevated_role: {
|
has_more_elevated_role: {
|
||||||
Args: {
|
Args: {
|
||||||
target_user_id: string
|
target_user_id: string
|
||||||
@@ -1590,10 +1526,6 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
has_personal_code: {
|
|
||||||
Args: { account_id: string }
|
|
||||||
Returns: boolean
|
|
||||||
}
|
|
||||||
has_role_on_account: {
|
has_role_on_account: {
|
||||||
Args: { account_id: string; account_role?: string }
|
Args: { account_id: string; account_role?: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
@@ -1606,6 +1538,10 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
|
has_unseen_membership_confirmation: {
|
||||||
|
Args: { p_user_id?: string }
|
||||||
|
Returns: boolean
|
||||||
|
}
|
||||||
is_aal2: {
|
is_aal2: {
|
||||||
Args: Record<PropertyKey, never>
|
Args: Record<PropertyKey, never>
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
|
|||||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -196,8 +196,8 @@ importers:
|
|||||||
specifier: ^3.5.3
|
specifier: ^3.5.3
|
||||||
version: 3.5.3
|
version: 3.5.3
|
||||||
supabase:
|
supabase:
|
||||||
specifier: ^2.26.9
|
specifier: ^2.30.4
|
||||||
version: 2.26.9
|
version: 2.30.4
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: 4.1.7
|
specifier: 4.1.7
|
||||||
version: 4.1.7
|
version: 4.1.7
|
||||||
@@ -7296,8 +7296,8 @@ packages:
|
|||||||
stylis@4.2.0:
|
stylis@4.2.0:
|
||||||
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
|
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
|
||||||
|
|
||||||
supabase@2.26.9:
|
supabase@2.30.4:
|
||||||
resolution: {integrity: sha512-wHl7HtAD2iHMVXL8JZyfSjdI0WYM7EF0ydThp1tSvDANaD2JHCZc8GH1NdzglbwGqdHmjCYeSZ+H28fmucYl7Q==}
|
resolution: {integrity: sha512-AOCyd2vmBBMTXbnahiCU0reRNxKS4n5CrPciUF2tcTrQ8dLzl1HwcLfe5DrG8E0QRcKHPDdzprmh/2+y4Ta5MA==}
|
||||||
engines: {npm: '>=8'}
|
engines: {npm: '>=8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -14753,7 +14753,7 @@ snapshots:
|
|||||||
|
|
||||||
stylis@4.2.0: {}
|
stylis@4.2.0: {}
|
||||||
|
|
||||||
supabase@2.26.9:
|
supabase@2.30.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
bin-links: 5.0.0
|
bin-links: 5.0.0
|
||||||
https-proxy-agent: 7.0.6
|
https-proxy-agent: 7.0.6
|
||||||
|
|||||||
@@ -126,10 +126,7 @@
|
|||||||
"description": "Jätkamiseks palun sisestage enda isikuandmed",
|
"description": "Jätkamiseks palun sisestage enda isikuandmed",
|
||||||
"button": "Jätka",
|
"button": "Jätka",
|
||||||
"userConsentLabel": "Nõustun isikuandmete kasutamisega platvormil",
|
"userConsentLabel": "Nõustun isikuandmete kasutamisega platvormil",
|
||||||
"userConsentUrlTitle": "Vaata isikuandmete töötlemise põhimõtteid",
|
"userConsentUrlTitle": "Vaata isikuandmete töötlemise põhimõtteid"
|
||||||
"successTitle": "Tere, {{firstName}} {{lastName}}",
|
|
||||||
"successDescription": "Teie tervisekonto on aktiveeritud ja kasutamiseks valmis!",
|
|
||||||
"successButton": "Jätka"
|
|
||||||
},
|
},
|
||||||
"consentModal": {
|
"consentModal": {
|
||||||
"title": "Enne toimetama hakkamist",
|
"title": "Enne toimetama hakkamist",
|
||||||
@@ -143,5 +140,10 @@
|
|||||||
"consentToAnonymizedCompanyData": {
|
"consentToAnonymizedCompanyData": {
|
||||||
"label": "Nõustun osalema tööandja statistikas",
|
"label": "Nõustun osalema tööandja statistikas",
|
||||||
"description": "Nõustun anonümiseeritud kujul terviseandmete kasutamisega tööandja statistikas"
|
"description": "Nõustun anonümiseeritud kujul terviseandmete kasutamisega tööandja statistikas"
|
||||||
|
},
|
||||||
|
"membershipConfirmation": {
|
||||||
|
"successTitle": "Tere, {{firstName}} {{lastName}}",
|
||||||
|
"successDescription": "Teie tervisekonto on aktiveeritud ja kasutamiseks valmis!",
|
||||||
|
"successButton": "Jätka"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
alter table "public"."accounts_memberships" add column "has_seen_confirmation" boolean not null default false;
|
||||||
|
|
||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.has_unseen_membership_confirmation(p_user_id uuid DEFAULT auth.uid())
|
||||||
|
RETURNS boolean
|
||||||
|
LANGUAGE sql
|
||||||
|
SECURITY DEFINER
|
||||||
|
SET search_path TO 'public', 'extensions'
|
||||||
|
AS $function$
|
||||||
|
select exists (
|
||||||
|
select 1
|
||||||
|
from public.accounts_memberships am
|
||||||
|
where am.user_id = p_user_id
|
||||||
|
and am.has_seen_confirmation = false
|
||||||
|
);
|
||||||
|
$function$
|
||||||
|
;
|
||||||
|
|
||||||
|
grant execute on function public.has_unseen_membership_confirmation(uuid)
|
||||||
|
to authenticated, anon;
|
||||||
|
|
||||||
|
drop function if exists "public"."has_personal_code"(account_id uuid);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.has_consent_personal_data(account_id uuid)
|
||||||
|
RETURNS boolean
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER
|
||||||
|
SET search_path = ''
|
||||||
|
AS $function$BEGIN
|
||||||
|
RETURN EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM public.accounts
|
||||||
|
WHERE id = account_id
|
||||||
|
AND has_consent_personal_data IS TRUE
|
||||||
|
);
|
||||||
|
END;$function$
|
||||||
|
;
|
||||||
|
|
||||||
|
grant execute on function public.has_consent_personal_data(uuid)
|
||||||
|
to authenticated, anon;
|
||||||
Reference in New Issue
Block a user