Merge branch 'main' into B2B-30
This commit is contained in:
@@ -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.
|
||||
Here you can define email templates using React components and export them as a function that returns the email content.
|
||||
|
||||
90
packages/email-templates/src/emails/company-offer.email.tsx
Normal file
90
packages/email-templates/src/emails/company-offer.email.tsx
Normal file
@@ -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(
|
||||
<Html>
|
||||
<Head>
|
||||
<BodyStyle />
|
||||
</Head>
|
||||
|
||||
<Preview>{previewText}</Preview>
|
||||
|
||||
<Tailwind>
|
||||
<Body>
|
||||
<EmailWrapper>
|
||||
<EmailHeader>
|
||||
<EmailHeading>{previewText}</EmailHeading>
|
||||
</EmailHeader>
|
||||
|
||||
<EmailContent>
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:companyName`)} {companyData.companyName}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:contactPerson`)} {companyData.contactPerson}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:email`)} {companyData.email}
|
||||
</Text>
|
||||
|
||||
<Text className="text-[16px] leading-[24px] text-[#242424]">
|
||||
{t(`${namespace}:phone`)} {companyData.phone || 'N/A'}
|
||||
</Text>
|
||||
</EmailContent>
|
||||
</EmailWrapper>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>,
|
||||
);
|
||||
|
||||
return {
|
||||
html,
|
||||
subject,
|
||||
to,
|
||||
};
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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:"
|
||||
}
|
||||
@@ -26,7 +26,8 @@ export function usePersonalAccountData(
|
||||
`
|
||||
id,
|
||||
name,
|
||||
picture_url
|
||||
picture_url,
|
||||
last_name
|
||||
`,
|
||||
)
|
||||
.eq('primary_owner_user_id', userId)
|
||||
|
||||
@@ -215,7 +215,6 @@ async function TeamAccountPage(props: {
|
||||
|
||||
<div>
|
||||
<div className={'flex flex-col gap-y-8'}>
|
||||
|
||||
<div className={'flex flex-col gap-y-2.5'}>
|
||||
<Heading level={6}>Company Members</Heading>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export function SignUpMethodsContainer(props: {
|
||||
paths: {
|
||||
callback: string;
|
||||
appHome: string;
|
||||
updateAccount: string;
|
||||
};
|
||||
|
||||
providers: {
|
||||
|
||||
225
packages/features/auth/src/components/update-account-form.tsx
Normal file
225
packages/features/auth/src/components/update-account-form.tsx
Normal file
@@ -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 (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="flex flex-col gap-6 px-6 pt-10 text-left"
|
||||
onSubmit={form.handleSubmit(onUpdateAccount)}
|
||||
>
|
||||
<FormField
|
||||
name="firstName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:firstName'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="lastName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:lastName'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="personalCode"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:personalCode'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:email'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} disabled />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="phone"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:phone'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="city"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:city'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex flex-row justify-between gap-4">
|
||||
<FormField
|
||||
name="weight"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:weight'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="kg"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
name="height"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'common:formField:height'} />
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="cm"
|
||||
type="number"
|
||||
{...field}
|
||||
value={field.value ?? ''}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
name="userConsent"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex flex-row items-center gap-2 pb-1">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>
|
||||
<Trans i18nKey={'account:updateAccount:userConsentLabel'} />
|
||||
</FormLabel>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={''}
|
||||
className="flex flex-row items-center gap-2 text-sm hover:underline"
|
||||
target="_blank"
|
||||
>
|
||||
<ExternalLink />
|
||||
<Trans i18nKey={'account:updateAccount:userConsentUrlTitle'} />
|
||||
</Link>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button disabled={form.formState.isSubmitting} type="submit">
|
||||
<Trans i18nKey={'account:updateAccount:button'} />
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
44
packages/features/auth/src/schemas/update-account.schema.ts
Normal file
44
packages/features/auth/src/schemas/update-account.schema.ts
Normal file
@@ -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',
|
||||
}),
|
||||
});
|
||||
@@ -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,
|
||||
},
|
||||
);
|
||||
93
packages/features/auth/src/server/api.ts
Normal file
93
packages/features/auth/src/server/api.ts
Normal file
@@ -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<Database>} client - The Supabase client instance.
|
||||
*/
|
||||
class AuthApi {
|
||||
constructor(private readonly client: SupabaseClient<Database>) {}
|
||||
|
||||
/**
|
||||
* @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<Database>) {
|
||||
return new AuthApi(client);
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './components/sign-up-methods-container';
|
||||
export * from './schemas/password-sign-up.schema';
|
||||
export * from './components/update-account-form';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@kit/tsconfig/base.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './notifications-popover';
|
||||
export * from './success-notification';
|
||||
export * from './update-account-success-notification';
|
||||
|
||||
@@ -126,7 +126,7 @@ export function NotificationsPopover(params: {
|
||||
|
||||
<span
|
||||
className={cn(
|
||||
`fade-in animate-in zoom-in absolute right-1 top-1 mt-0 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
|
||||
`fade-in animate-in zoom-in absolute top-1 right-1 mt-0 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[0.65rem] text-white`,
|
||||
{
|
||||
hidden: !notifications.length,
|
||||
},
|
||||
@@ -186,7 +186,7 @@ export function NotificationsPopover(params: {
|
||||
<div
|
||||
key={notification.id.toString()}
|
||||
className={cn(
|
||||
'min-h-18 flex flex-col items-start justify-center gap-y-1 px-3 py-2',
|
||||
'flex min-h-18 flex-col items-start justify-center gap-y-1 px-3 py-2',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (params.onClick) {
|
||||
|
||||
@@ -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 (
|
||||
<div className="border-border rounded-3xl border px-16 pt-4 pb-12">
|
||||
{showLogo && <MedReportLogo />}
|
||||
<div className="flex flex-col items-center px-4">
|
||||
<Image
|
||||
src="/assets/success.png"
|
||||
alt="Success"
|
||||
className="pt-6 pb-8"
|
||||
width={326}
|
||||
height={195}
|
||||
/>
|
||||
<h1 className="pb-2">{title || <Trans i18nKey={titleKey} />}</h1>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey={descriptionKey} />
|
||||
</p>
|
||||
</div>
|
||||
{buttonProps && (
|
||||
<Button className="mt-8 w-full">
|
||||
<Link href={buttonProps.href}>
|
||||
<Trans i18nKey={buttonProps.buttonTitleKey} />
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -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 (
|
||||
<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.home,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@kit/tsconfig/base.json",
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -48,6 +48,48 @@ export type Database = {
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
request_entries: {
|
||||
Row: {
|
||||
comment: string | null
|
||||
created_at: string
|
||||
id: number
|
||||
personal_code: number | null
|
||||
request_api: string
|
||||
request_api_method: string
|
||||
requested_end_date: string | null
|
||||
requested_start_date: string | null
|
||||
service_id: number | null
|
||||
service_provider_id: number | null
|
||||
status: Database["audit"]["Enums"]["request_status"]
|
||||
}
|
||||
Insert: {
|
||||
comment?: string | null
|
||||
created_at?: string
|
||||
id?: number
|
||||
personal_code?: number | null
|
||||
request_api: string
|
||||
request_api_method: string
|
||||
requested_end_date?: string | null
|
||||
requested_start_date?: string | null
|
||||
service_id?: number | null
|
||||
service_provider_id?: number | null
|
||||
status: Database["audit"]["Enums"]["request_status"]
|
||||
}
|
||||
Update: {
|
||||
comment?: string | null
|
||||
created_at?: string
|
||||
id?: number
|
||||
personal_code?: number | null
|
||||
request_api?: string
|
||||
request_api_method?: string
|
||||
requested_end_date?: string | null
|
||||
requested_start_date?: string | null
|
||||
service_id?: number | null
|
||||
service_provider_id?: number | null
|
||||
status?: Database["audit"]["Enums"]["request_status"]
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
sync_entries: {
|
||||
Row: {
|
||||
changed_by_role: string
|
||||
@@ -83,6 +125,7 @@ export type Database = {
|
||||
[_ in never]: never
|
||||
}
|
||||
Enums: {
|
||||
request_status: "SUCCESS" | "FAIL"
|
||||
sync_status: "SUCCESS" | "FAIL"
|
||||
}
|
||||
CompositeTypes: {
|
||||
@@ -116,15 +159,43 @@ export type Database = {
|
||||
}
|
||||
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: {
|
||||
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
|
||||
@@ -133,13 +204,17 @@ export type Database = {
|
||||
updated_by: string | null
|
||||
}
|
||||
Insert: {
|
||||
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
|
||||
@@ -148,13 +223,17 @@ export type Database = {
|
||||
updated_by?: string | null
|
||||
}
|
||||
Update: {
|
||||
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
|
||||
@@ -200,20 +279,6 @@ export type Database = {
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "accounts_memberships_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "accounts_memberships_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_personal_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "accounts_memberships_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
@@ -515,20 +580,6 @@ export type Database = {
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "billing_customers_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "billing_customers_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_personal_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "billing_customers_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
@@ -627,6 +678,158 @@ export type Database = {
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
connected_online_providers: {
|
||||
Row: {
|
||||
can_select_worker: boolean
|
||||
created_at: string
|
||||
email: string | null
|
||||
id: number
|
||||
name: string
|
||||
personal_code_required: boolean
|
||||
phone_number: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
can_select_worker: boolean
|
||||
created_at?: string
|
||||
email?: string | null
|
||||
id: number
|
||||
name: string
|
||||
personal_code_required: boolean
|
||||
phone_number?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
can_select_worker?: boolean
|
||||
created_at?: string
|
||||
email?: string | null
|
||||
id?: number
|
||||
name?: string
|
||||
personal_code_required?: boolean
|
||||
phone_number?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
connected_online_reservation: {
|
||||
Row: {
|
||||
booking_code: string
|
||||
clinic_id: number
|
||||
comments: string | null
|
||||
created_at: string
|
||||
discount_code: string | null
|
||||
id: number
|
||||
lang: string
|
||||
requires_payment: boolean
|
||||
service_id: number
|
||||
service_user_id: number | null
|
||||
start_time: string
|
||||
sync_user_id: number
|
||||
updated_at: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
booking_code: string
|
||||
clinic_id: number
|
||||
comments?: string | null
|
||||
created_at?: string
|
||||
discount_code?: string | null
|
||||
id?: number
|
||||
lang: string
|
||||
requires_payment: boolean
|
||||
service_id: number
|
||||
service_user_id?: number | null
|
||||
start_time: string
|
||||
sync_user_id: number
|
||||
updated_at?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
booking_code?: string
|
||||
clinic_id?: number
|
||||
comments?: string | null
|
||||
created_at?: string
|
||||
discount_code?: string | null
|
||||
id?: number
|
||||
lang?: string
|
||||
requires_payment?: boolean
|
||||
service_id?: number
|
||||
service_user_id?: number | null
|
||||
start_time?: string
|
||||
sync_user_id?: number
|
||||
updated_at?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
connected_online_services: {
|
||||
Row: {
|
||||
clinic_id: number
|
||||
code: string
|
||||
created_at: string
|
||||
description: string | null
|
||||
display: string | null
|
||||
duration: number
|
||||
has_free_codes: boolean
|
||||
id: number
|
||||
name: string
|
||||
neto_duration: number | null
|
||||
online_hide_duration: number | null
|
||||
online_hide_price: number | null
|
||||
price: number
|
||||
price_periods: string | null
|
||||
requires_payment: boolean
|
||||
sync_id: number
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
clinic_id: number
|
||||
code: string
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
display?: string | null
|
||||
duration: number
|
||||
has_free_codes: boolean
|
||||
id: number
|
||||
name: string
|
||||
neto_duration?: number | null
|
||||
online_hide_duration?: number | null
|
||||
online_hide_price?: number | null
|
||||
price: number
|
||||
price_periods?: string | null
|
||||
requires_payment: boolean
|
||||
sync_id: number
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
clinic_id?: number
|
||||
code?: string
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
display?: string | null
|
||||
duration?: number
|
||||
has_free_codes?: boolean
|
||||
id?: number
|
||||
name?: string
|
||||
neto_duration?: number | null
|
||||
online_hide_duration?: number | null
|
||||
online_hide_price?: number | null
|
||||
price?: number
|
||||
price_periods?: string | null
|
||||
requires_payment?: boolean
|
||||
sync_id?: number
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "connected_online_services_clinic_id_fkey"
|
||||
columns: ["clinic_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "connected_online_providers"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
invitations: {
|
||||
Row: {
|
||||
account_id: string
|
||||
@@ -672,20 +875,6 @@ export type Database = {
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "invitations_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "invitations_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_personal_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "invitations_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
@@ -709,6 +898,129 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
medreport_product_groups: {
|
||||
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: {
|
||||
analysis_element_id: number | null
|
||||
analysis_id: number | null
|
||||
product_id: number
|
||||
}
|
||||
Insert: {
|
||||
analysis_element_id?: number | null
|
||||
analysis_id?: number | null
|
||||
product_id: number
|
||||
}
|
||||
Update: {
|
||||
analysis_element_id?: number | null
|
||||
analysis_id?: number | null
|
||||
product_id?: number
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "medreport_products_analyses_analysis_element_id_fkey"
|
||||
columns: ["analysis_element_id"]
|
||||
isOneToOne: true
|
||||
referencedRelation: "analysis_elements"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "medreport_products_analyses_analysis_id_fkey"
|
||||
columns: ["analysis_id"]
|
||||
isOneToOne: true
|
||||
referencedRelation: "analyses"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "medreport_products_analyses_product_id_fkey"
|
||||
columns: ["product_id"]
|
||||
isOneToOne: true
|
||||
referencedRelation: "medreport_products"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
medreport_products_external_services_relations: {
|
||||
Row: {
|
||||
connected_online_service_id: number
|
||||
product_id: number
|
||||
}
|
||||
Insert: {
|
||||
connected_online_service_id: number
|
||||
product_id: number
|
||||
}
|
||||
Update: {
|
||||
connected_online_service_id?: number
|
||||
product_id?: number
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "medreport_products_connected_online_services_id_fkey"
|
||||
columns: ["connected_online_service_id"]
|
||||
isOneToOne: true
|
||||
referencedRelation: "connected_online_services"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "medreport_products_connected_online_services_product_id_fkey"
|
||||
columns: ["product_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "medreport_products"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
nonces: {
|
||||
Row: {
|
||||
client_token: string
|
||||
@@ -808,20 +1120,6 @@ export type Database = {
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "notifications_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "notifications_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_personal_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "notifications_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
@@ -921,20 +1219,6 @@ export type Database = {
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "orders_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "orders_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_personal_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "orders_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
@@ -1106,20 +1390,6 @@ export type Database = {
|
||||
referencedRelation: "accounts"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "subscriptions_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "subscriptions_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "invitations_with_personal_accounts"
|
||||
referencedColumns: ["account_id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "subscriptions_account_id_fkey"
|
||||
columns: ["account_id"]
|
||||
@@ -1145,23 +1415,6 @@ export type Database = {
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
invitations_with_accounts: {
|
||||
Row: {
|
||||
account_id: string | null
|
||||
invite_token: string | null
|
||||
personal_code: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
invitations_with_personal_accounts: {
|
||||
Row: {
|
||||
account_id: string | null
|
||||
account_slug: string | null
|
||||
invite_token: string | null
|
||||
personal_code: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
user_account_workspace: {
|
||||
Row: {
|
||||
id: string | null
|
||||
@@ -1241,13 +1494,17 @@ export type Database = {
|
||||
create_team_account: {
|
||||
Args: { account_name: string }
|
||||
Returns: {
|
||||
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
|
||||
@@ -1329,6 +1586,10 @@ export type Database = {
|
||||
}
|
||||
Returns: boolean
|
||||
}
|
||||
has_personal_code: {
|
||||
Args: { account_id: string }
|
||||
Returns: boolean
|
||||
}
|
||||
has_role_on_account: {
|
||||
Args: { account_id: string; account_role?: string }
|
||||
Returns: boolean
|
||||
@@ -1395,6 +1656,18 @@ export type Database = {
|
||||
Args: { target_account_id: string; new_owner_id: string }
|
||||
Returns: undefined
|
||||
}
|
||||
update_account: {
|
||||
Args: {
|
||||
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
|
||||
@@ -1611,6 +1884,7 @@ export type CompositeTypes<
|
||||
export const Constants = {
|
||||
audit: {
|
||||
Enums: {
|
||||
request_status: ["SUCCESS", "FAIL"],
|
||||
sync_status: ["SUCCESS", "FAIL"],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@ const RouteChild = z.object({
|
||||
});
|
||||
|
||||
const RouteGroup = z.object({
|
||||
label: z.string(),
|
||||
label: z.string().optional(),
|
||||
collapsible: z.boolean().optional(),
|
||||
collapsed: z.boolean().optional(),
|
||||
children: z.array(RouteChild),
|
||||
@@ -37,12 +37,8 @@ const RouteGroup = z.object({
|
||||
});
|
||||
|
||||
export const NavigationConfigSchema = z.object({
|
||||
style: z.enum(['custom', 'sidebar', 'header']).default('sidebar'),
|
||||
sidebarCollapsed: z
|
||||
.enum(['false', 'true'])
|
||||
.default('true')
|
||||
.optional()
|
||||
.transform((value) => value === `true`),
|
||||
style: z.enum(['custom', 'sidebar', 'header']).default('custom'),
|
||||
sidebarCollapsed: z.boolean().optional(),
|
||||
sidebarCollapsedStyle: z.enum(['offcanvas', 'icon', 'none']).default('icon'),
|
||||
routes: z.array(z.union([RouteGroup, Divider])),
|
||||
});
|
||||
|
||||
@@ -14,10 +14,6 @@ type PageProps = React.PropsWithChildren<{
|
||||
sticky?: boolean;
|
||||
}>;
|
||||
|
||||
const ENABLE_SIDEBAR_TRIGGER = process.env.NEXT_PUBLIC_ENABLE_SIDEBAR_TRIGGER
|
||||
? process.env.NEXT_PUBLIC_ENABLE_SIDEBAR_TRIGGER === 'true'
|
||||
: true;
|
||||
|
||||
export function Page(props: PageProps) {
|
||||
switch (props.style) {
|
||||
case 'header':
|
||||
@@ -79,7 +75,7 @@ function PageWithHeader(props: PageProps) {
|
||||
const { Navigation, Children, MobileNavigation } = getSlotsFromPage(props);
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-screen flex-1 flex-col', props.className)}>
|
||||
<div className={cn('flex h-screen flex-1 flex-col z-1000', props.className)}>
|
||||
<div
|
||||
className={
|
||||
props.contentContainerClassName ?? 'flex flex-1 flex-col space-y-4'
|
||||
@@ -87,9 +83,9 @@ function PageWithHeader(props: PageProps) {
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-bg-background border-1 light:border-border dark:border-border dark:shadow-primary/10 flex h-15 items-center justify-between px-4 py-1 lg:justify-start lg:shadow-xs',
|
||||
'bg-bg-background border-1 light:border-border dark:border-border dark:shadow-primary/10 flex h-15 items-center justify-between px-4 py-1 lg:justify-start lg:shadow-xs border-b',
|
||||
{
|
||||
'sticky top-0 z-10 backdrop-blur-md': props.sticky ?? true,
|
||||
'sticky top-0 z-1000 backdrop-blur-md': props.sticky ?? true,
|
||||
},
|
||||
)}
|
||||
>
|
||||
@@ -113,7 +109,10 @@ export function PageBody(
|
||||
className?: string;
|
||||
}>,
|
||||
) {
|
||||
const className = cn('flex w-full flex-1 flex-col lg:px-4', props.className);
|
||||
const className = cn(
|
||||
'flex w-full flex-1 flex-col space-y-6 lg:px-4',
|
||||
props.className,
|
||||
);
|
||||
|
||||
return <div className={className}>{props.children}</div>;
|
||||
}
|
||||
@@ -125,7 +124,7 @@ export function PageNavigation(props: React.PropsWithChildren) {
|
||||
export function PageDescription(props: React.PropsWithChildren) {
|
||||
return (
|
||||
<div className={'flex h-6 items-center'}>
|
||||
<div className={'text-muted-foreground text-xs leading-none font-normal'}>
|
||||
<div className={'text-muted-foreground text-sm leading-none font-normal'}>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
@@ -153,7 +152,7 @@ export function PageHeader({
|
||||
title,
|
||||
description,
|
||||
className,
|
||||
displaySidebarTrigger = ENABLE_SIDEBAR_TRIGGER,
|
||||
displaySidebarTrigger = false,
|
||||
}: React.PropsWithChildren<{
|
||||
className?: string;
|
||||
title?: string | React.ReactNode;
|
||||
|
||||
@@ -70,7 +70,7 @@ export function VersionUpdater(props: { intervalTimeInSecond?: number }) {
|
||||
setDismissed(true);
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="common:back" />
|
||||
<Trans i18nKey="common:goBack" />
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => window.location.reload()}>
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { VariantProps } from 'class-variance-authority';
|
||||
import { cn } from '../lib/utils';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'focus-visible:ring-ring inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',
|
||||
'focus-visible:ring-ring gap-1 inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../lib/utils';
|
||||
import { VariantProps, cva } from 'class-variance-authority';
|
||||
import { cn } from '.';
|
||||
|
||||
const Card: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
|
||||
className,
|
||||
...props
|
||||
}) => (
|
||||
<div
|
||||
className={cn('bg-card text-card-foreground rounded-xl border', className)}
|
||||
{...props}
|
||||
/>
|
||||
const cardVariants = cva('text-card-foreground rounded-xl border', {
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-card',
|
||||
'gradient-warning':
|
||||
'from-warning/30 via-warning/10 to-background bg-gradient-to-t',
|
||||
'gradient-destructive':
|
||||
'from-destructive/30 via-destructive/10 to-background bg-gradient-to-t',
|
||||
'gradient-success':
|
||||
'from-success/30 via-success/10 to-background bg-gradient-to-t',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
});
|
||||
|
||||
export interface CardProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof cardVariants> {}
|
||||
|
||||
const Card: React.FC<CardProps> = ({ className, variant, ...props }) => (
|
||||
<div className={cn(cardVariants({ variant, className }))} {...props} />
|
||||
);
|
||||
Card.displayName = 'Card';
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const Checkbox: React.FC<
|
||||
> = ({ className, ...props }) => (
|
||||
<CheckboxPrimitive.Root
|
||||
className={cn(
|
||||
'peer border-primary focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground h-4 w-4 shrink-0 rounded-xs border shadow-xs focus-visible:ring-1 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'peer border-primary focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground h-4 w-4 shrink-0 rounded-sm border shadow-xs focus-visible:ring-1 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
3
packages/ui/src/shadcn/constants.ts
Normal file
3
packages/ui/src/shadcn/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const SIDEBAR_WIDTH = '16rem';
|
||||
export const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||
export const SIDEBAR_WIDTH_ICON = '4rem';
|
||||
@@ -21,6 +21,11 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from './collapsible';
|
||||
import {
|
||||
SIDEBAR_WIDTH,
|
||||
SIDEBAR_WIDTH_ICON,
|
||||
SIDEBAR_WIDTH_MOBILE,
|
||||
} from './constants';
|
||||
import { Input } from './input';
|
||||
import { Separator } from './separator';
|
||||
import { Sheet, SheetContent } from './sheet';
|
||||
@@ -34,9 +39,6 @@ import {
|
||||
|
||||
const SIDEBAR_COOKIE_NAME = 'sidebar:state';
|
||||
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||
const SIDEBAR_WIDTH = '16rem';
|
||||
const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||
const SIDEBAR_WIDTH_ICON = '4rem';
|
||||
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
||||
const SIDEBAR_MINIMIZED_WIDTH = SIDEBAR_WIDTH_ICON;
|
||||
|
||||
@@ -276,7 +278,7 @@ const Sidebar: React.FC<
|
||||
<div
|
||||
data-sidebar="sidebar"
|
||||
className={cn(
|
||||
'bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm',
|
||||
'bg-sidebar group-data-[variant=floating]:border-sidebar-border ml-3 flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm',
|
||||
{
|
||||
'bg-transparent': variant === 'ghost',
|
||||
},
|
||||
@@ -908,7 +910,7 @@ export function SidebarNavigation({
|
||||
tooltip={child.label}
|
||||
>
|
||||
<Link
|
||||
className={cn('flex items-center', {
|
||||
className={cn('flex items-center font-medium', {
|
||||
'mx-auto w-full gap-0! [&>svg]:flex-1': !open,
|
||||
})}
|
||||
href={path}
|
||||
@@ -916,7 +918,7 @@ export function SidebarNavigation({
|
||||
{child.Icon}
|
||||
<span
|
||||
className={cn(
|
||||
'w-auto transition-opacity duration-300',
|
||||
'text-md w-auto font-medium transition-opacity duration-300',
|
||||
{
|
||||
'w-0 opacity-0': !open,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@kit/tsconfig/base.json",
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
|
||||
"paths": {
|
||||
@@ -13,4 +13,4 @@
|
||||
},
|
||||
"include": ["*.ts", "src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user