diff --git a/legalconsenthub/components/CreateOrganizationModal.vue b/legalconsenthub/components/CreateOrganizationModal.vue index 8e9a5d5..165eeb3 100644 --- a/legalconsenthub/components/CreateOrganizationModal.vue +++ b/legalconsenthub/components/CreateOrganizationModal.vue @@ -9,7 +9,7 @@ - + @@ -33,25 +33,14 @@ - - diff --git a/legalconsenthub/components/Register.vue b/legalconsenthub/components/Register.vue deleted file mode 100644 index c7c4d11..0000000 --- a/legalconsenthub/components/Register.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - Registrieren - - - Benutzername: - - - - - Email: - - - - - Passwort: - - - - - Passwort bestätigen: - - - - Registrieren - - - - - - - diff --git a/legalconsenthub/composables/organization/useOrganizationApi.ts b/legalconsenthub/composables/organization/useOrganizationApi.ts index fdb9884..eda8cac 100644 --- a/legalconsenthub/composables/organization/useOrganizationApi.ts +++ b/legalconsenthub/composables/organization/useOrganizationApi.ts @@ -1,5 +1,5 @@ import type { LegalRole } from '~/server/utils/permissions' -import type { ListMembersOptions } from '~/composables/useAuth' +import type { ListMembersOptions } from '~/types/auth' export function useOrganizationApi() { const { organization } = useAuth() diff --git a/legalconsenthub/composables/useAuth.ts b/legalconsenthub/composables/useAuth.ts index 6366739..fed8899 100644 --- a/legalconsenthub/composables/useAuth.ts +++ b/legalconsenthub/composables/useAuth.ts @@ -2,47 +2,20 @@ import { defu } from 'defu' import { createAuthClient } from 'better-auth/vue' -import type { InferSessionFromClient, InferUserFromClient, ClientOptions } from 'better-auth/client' -import { organizationClient, jwtClient } from 'better-auth/client/plugins' +import type { ClientOptions, InferSessionFromClient, InferUserFromClient } from 'better-auth/client' +import { jwtClient, organizationClient } from 'better-auth/client/plugins' import type { RouteLocationRaw } from 'vue-router' import { accessControl, - employerRole, - worksCouncilMemberRole, - employeeRole, adminRole, + employeeRole, + employerRole, ownerRole, - ROLES + ROLES, + worksCouncilMemberRole } from '~/server/utils/permissions' +import type { RuntimeAuthConfig } from '~/types/auth' -interface RuntimeAuthConfig { - redirectUserTo: RouteLocationRaw | string - redirectGuestTo: RouteLocationRaw | string -} - -// Types can be found here: https://github.com/better-auth/better-auth/blob/3f574ec70bb15c155a78673d42c5e25f7376ced3/packages/better-auth/src/plugins/organization/routes/crud-invites.ts#L531 -type Client = ReturnType['client'] - -export type Session = Client['$Infer']['Session'] -export type User = Session['user'] -export type ActiveOrganization = Client['$Infer']['ActiveOrganization'] -export type Organization = Client['$Infer']['Organization'] -export type Invitation = Client['$Infer']['Invitation'] -export type Member = Client['$Infer']['Member'] -export type ListMembersOptions = Parameters[0] -export type ListMembersResponse = Awaited> -export type ListMembersQuery = NonNullable['query'] - -// Extended invitation type with additional organization and inviter details -export type CustomInvitation = - | (Invitation & { - organizationName: string - organizationSlug: string - inviterEmail: string - }) - | null - -// TODO: Move into pinia store const session = ref | null>(null) const user = ref | null>(null) const sessionFetching = import.meta.server ? ref(false) : ref(false) @@ -195,7 +168,6 @@ export function useAuth() { user, loggedIn: computed(() => !!session.value), signIn: client.signIn, - signUp: client.signUp, deleteUser: client.deleteUser, signOut, organization: client.organization, diff --git a/legalconsenthub/pages/accept-invitation/[id].vue b/legalconsenthub/pages/accept-invitation/[id].vue index a8026f3..a17cadc 100644 --- a/legalconsenthub/pages/accept-invitation/[id].vue +++ b/legalconsenthub/pages/accept-invitation/[id].vue @@ -69,7 +69,7 @@ diff --git a/legalconsenthub/pages/signup.vue b/legalconsenthub/pages/signup.vue index a768ba1..4b663e0 100644 --- a/legalconsenthub/pages/signup.vue +++ b/legalconsenthub/pages/signup.vue @@ -1,11 +1,11 @@ Already have an account? Login. @@ -18,17 +18,15 @@ diff --git a/legalconsenthub/stores/useOrganizationStore.ts b/legalconsenthub/stores/useOrganizationStore.ts index 72f5877..b8bdf9d 100644 --- a/legalconsenthub/stores/useOrganizationStore.ts +++ b/legalconsenthub/stores/useOrganizationStore.ts @@ -1,13 +1,15 @@ -import type { - ActiveOrganization, - Organization, - Invitation, - ListMembersOptions, - ListMembersResponse, - ListMembersQuery -} from '~/composables/useAuth' import { useOrganizationApi } from '~/composables/organization/useOrganizationApi' import type { LegalRole } from '~/server/utils/permissions' +import type { + ActiveOrganization, + CustomInvitation, + Invitation, + ListMembersOptions, + ListMembersQuery, + ListMembersResponse, + Member, + Organization +} from '~/types/auth' export const useOrganizationStore = defineStore('Organization', () => { const activeOrganization = ref(null) diff --git a/legalconsenthub/stores/useUserStore.ts b/legalconsenthub/stores/useUserStore.ts index b684435..6ea512d 100644 --- a/legalconsenthub/stores/useUserStore.ts +++ b/legalconsenthub/stores/useUserStore.ts @@ -1,39 +1,82 @@ import type { FormSubmitEvent } from '@nuxt/ui' +import { UserRole } from '~/.api-client' +import type { SignInSchema, SignUpSchema } from '~/types/schemas' export const useUserStore = defineStore('User', () => { + const { createUser } = useUser() const name = ref('') + const toast = useToast() + const { client } = useAuth() - async function signUp(payload: FormSubmitEvent) { - await authClient.signUp.email( + async function signUp(payload: FormSubmitEvent) { + await client.signUp.email( { email: payload.data.email, password: payload.data.password, name: payload.data.name }, { - onRequest: (ctx) => { - // TODO: Show loading spinner + onRequest: () => { console.log('Sending register request') }, onResponse: () => { - // TODO: Hide loading spinner console.log('Receiving register response') }, onSuccess: async (ctx) => { console.log('Successfully registered!') + + // Create user in backend after successful Better Auth registration + try { + console.log('Creating user in backend...', ctx.data) + await createUser({ + id: ctx.data.user.id, + name: ctx.data.user.name, + status: 'ACTIVE', + role: UserRole.Employee + }) + console.log('User created in backend successfully') + } catch (error) { + console.error('Failed to create user in backend:', error) + toast.add({ + title: 'Warning', + description: 'Account created but there was an issue with backend setup. Please contact support.', + color: 'warning' + }) + } + await navigateTo('/') }, - onError: (ctx) => { + onError: async (ctx) => { console.log(ctx.error.message) useToast().add({ title: 'Fehler bei der Registrierung', description: ctx.error.message, - color: 'success' + color: 'error' }) } } ) } - return { name, signUp } + async function signIn(payload: FormSubmitEvent) { + await client.signIn.email( + { + email: payload.data.email, + password: payload.data.password + }, + { + onRequest: () => { + console.log('Sending login request') + }, + onSuccess: () => { + console.log('Successfully logged in!') + }, + onError: (ctx) => { + console.log(ctx.error.message) + } + } + ) + } + + return { name, signUp, signIn } }) diff --git a/legalconsenthub/types/auth.ts b/legalconsenthub/types/auth.ts new file mode 100644 index 0000000..ab9f39f --- /dev/null +++ b/legalconsenthub/types/auth.ts @@ -0,0 +1,27 @@ +import type { RouteLocationRaw } from '#vue-router' +import type { useAuth } from '~/composables/useAuth' + +export interface RuntimeAuthConfig { + redirectUserTo: RouteLocationRaw | string + redirectGuestTo: RouteLocationRaw | string +} + +// Types can be found here: https://github.com/better-auth/better-auth/blob/3f574ec70bb15c155a78673d42c5e25f7376ced3/packages/better-auth/src/plugins/organization/routes/crud-invites.ts#L531 +type Client = ReturnType['client'] +export type Session = Client['$Infer']['Session'] +export type User = Session['user'] +export type ActiveOrganization = Client['$Infer']['ActiveOrganization'] +export type Organization = Client['$Infer']['Organization'] +export type Invitation = Client['$Infer']['Invitation'] +export type Member = Client['$Infer']['Member'] +export type ListMembersOptions = Parameters[0] +export type ListMembersResponse = Awaited> +export type ListMembersQuery = NonNullable['query'] +// Extended invitation type with additional organization and inviter details +export type CustomInvitation = + | (Invitation & { + organizationName: string + organizationSlug: string + inviterEmail: string + }) + | null diff --git a/legalconsenthub/types/schemas.ts b/legalconsenthub/types/schemas.ts new file mode 100644 index 0000000..761de28 --- /dev/null +++ b/legalconsenthub/types/schemas.ts @@ -0,0 +1,22 @@ +import * as z from 'zod' + +export const signUpSchema = z.object({ + name: z.string().min(1, 'Name is required'), + email: z.string().email('Invalid email'), + password: z.string().min(8, 'Must be at least 8 characters') +}) + +export const signInSchema = z.object({ + email: z.string().email('Invalid email'), + password: z.string().min(8, 'Must be at least 8 characters') +}) + +export const organizationSchema = z.object({ + name: z.string().min(2, 'Too short'), + slug: z.string().min(2, 'Too short'), + logo: z.string().optional() +}) + +export type SignUpSchema = z.output +export type SignInSchema = z.output +export type OrganizationSchema = z.output