create utility types on server - include Account with Membership - start moving account functions to store actions

This commit is contained in:
Michael Dausmann
2023-02-20 00:21:56 +11:00
parent b3ee03b5c3
commit fbe2436231
6 changed files with 65 additions and 29 deletions

View File

@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { MembershipWithAccount } from '~~/lib/services/user.account.service';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
const supabase = useSupabaseAuthClient(); const supabase = useSupabaseAuthClient();
@@ -19,9 +20,18 @@ async function signout() {
<template> <template>
<div> <div>
<h3>Nuxt 3 Boilerplate - AppHeader</h3> <h3>Nuxt 3 Boilerplate - AppHeader</h3>
<!-- logged in & sign out -->
<div v-if="user">logged in as: {{ user.email }}: <button @click="signout()">Sign Out</button></div> <div v-if="user">logged in as: {{ user.email }}: <button @click="signout()">Sign Out</button></div>
<div v-if="!user">Not Logged in</div> <div v-if="!user">Not Logged in</div>
<button v-for="membership in dbUser?.dbUser.memberships" @click="store.changeActiveMembership(membership)">{{ membership.account_id }}<span v-if="membership.account_id === activeMembership?.account_id">*</span></button>
<!-- Account Switching -->
<p v-if="(dbUser?.dbUser?.memberships) && (dbUser.dbUser.memberships.length > 0)">
<span>Switch Account.. </span>
<button v-for="membership in dbUser?.dbUser.memberships" @click="store.changeActiveMembership(((membership as unknown) as MembershipWithAccount))"> <!-- This cast is infuriating -->
{{ membership.account.name }}
<span v-if="membership.account_id === activeMembership?.account_id">*</span>
</button>
</p>
<hr> <hr>
</div> </div>
</template> </template>

View File

@@ -1,8 +1,11 @@
import { ACCOUNT_ACCESS, PrismaClient, User as DBUser, Membership } from '@prisma/client'; import { ACCOUNT_ACCESS, PrismaClient, User, Membership, Account } from '@prisma/client';
import { UtilService } from './util.service'; import { UtilService } from './util.service';
const TRIAL_PLAN_NAME = '3 Month Trial'; // TODO - some sort of config.. this will change for every use of the boilerplate const TRIAL_PLAN_NAME = '3 Month Trial'; // TODO - some sort of config.. this will change for every use of the boilerplate
export type MembershipWithAccount = (Membership & {account: Account});
export type FullDBUser = (User & { memberships: MembershipWithAccount[]; });
export default class UserAccountService { export default class UserAccountService {
private prisma: PrismaClient; private prisma: PrismaClient;
@@ -10,15 +13,34 @@ export default class UserAccountService {
this.prisma = prisma; this.prisma = prisma;
} }
async getUserBySupabaseId(supabase_uid: string): Promise<(DBUser & { memberships: Membership[]; }) | null> { async getUserBySupabaseId(supabase_uid: string): Promise<FullDBUser | null> {
return this.prisma.user.findFirst({ where: { supabase_uid }, include: { memberships: true } }); return this.prisma.user.findFirst({
where: { supabase_uid },
include: { memberships: {include: {
account: true
}}}
});
} }
async getUserById(user_id: number) { async getFullUserBySupabaseId(supabase_uid: string): Promise<FullDBUser | null> {
return this.prisma.user.findFirstOrThrow({ where: { id: user_id }, include: { memberships: true } }); return this.prisma.user.findFirst({
where: { supabase_uid },
include: { memberships: {include: {
account: true
}}}
});
} }
async createUser( supabase_uid: string, display_name: string ) { async getUserById(user_id: number): Promise<FullDBUser | null> {
return this.prisma.user.findFirstOrThrow({
where: { id: user_id },
include: { memberships: {include: {
account: true
}}}
});
}
async createUser( supabase_uid: string, display_name: string ): Promise<FullDBUser | null> {
const trialPlan = await this.prisma.plan.findFirstOrThrow({ where: { name: TRIAL_PLAN_NAME}}); const trialPlan = await this.prisma.plan.findFirstOrThrow({ where: { name: TRIAL_PLAN_NAME}});
return this.prisma.user.create({ return this.prisma.user.create({
data:{ data:{
@@ -39,7 +61,9 @@ export default class UserAccountService {
} }
} }
}, },
include: { memberships: true }, include: { memberships: {include: {
account: true
}}}
}); });
} }

View File

@@ -14,11 +14,6 @@
await store.initUser(); await store.initUser();
}) })
async function changeAccountPlan(){
const { data: account } = await $client.userAccount.changeAccountPlan.useQuery();
console.log(`account with updated plan: ${JSON.stringify(account)}`);
}
async function joinUserToAccount(){ async function joinUserToAccount(){
const { data: membership } = await $client.userAccount.joinUserToAccount.useQuery(); const { data: membership } = await $client.userAccount.joinUserToAccount.useQuery();
console.log(`added membership on current account: ${JSON.stringify(membership)}`); console.log(`added membership on current account: ${JSON.stringify(membership)}`);
@@ -39,7 +34,7 @@
<h3>Notes Dashboard</h3> <h3>Notes Dashboard</h3>
<p v-for="note in notes">{{ note.note_text }}</p> <p v-for="note in notes">{{ note.note_text }}</p>
<button @click.prevent="changeAccountPlan()">Change Account Plan</button> <button @click.prevent="store.changeAccountPlan(2)">Change Account Plan to 2</button>
<button @click.prevent="joinUserToAccount()">Join user to account</button> <button @click.prevent="joinUserToAccount()">Join user to account</button>
<button @click.prevent="changeUserAccessWithinAccount()">Change user access within account</button> <button @click.prevent="changeUserAccessWithinAccount()">Change user access within account</button>
<button @click.prevent="claimOwnershipOfAccount()">Claim Account Ownership</button> <button @click.prevent="claimOwnershipOfAccount()">Claim Account Ownership</button>

View File

@@ -1,15 +1,15 @@
import { Membership, PrismaClient, User as DBUser } from '@prisma/client'; import { PrismaClient } from '@prisma/client';
import { inferAsyncReturnType, TRPCError } from '@trpc/server' import { inferAsyncReturnType, TRPCError } from '@trpc/server'
import { H3Event } from 'h3'; import { H3Event } from 'h3';
import { serverSupabaseClient } from '#supabase/server'; import { serverSupabaseClient } from '#supabase/server';
import SupabaseClient from '@supabase/supabase-js/dist/module/SupabaseClient'; import SupabaseClient from '@supabase/supabase-js/dist/module/SupabaseClient';
import { User } from '@supabase/supabase-js'; import { User } from '@supabase/supabase-js';
import UserAccountService from '~~/lib/services/user.account.service'; import UserAccountService, { FullDBUser } from '~~/lib/services/user.account.service';
let prisma: PrismaClient | undefined let prisma: PrismaClient | undefined
let supabase: SupabaseClient | undefined let supabase: SupabaseClient | undefined
let user: User | null; let user: User | null;
let dbUser: (DBUser & { memberships: Membership[]; }) | null let dbUser: FullDBUser | null
export async function createContext(event: H3Event){ export async function createContext(event: H3Event){
if (!supabase) { if (!supabase) {
@@ -23,7 +23,7 @@ export async function createContext(event: H3Event){
} }
if (!dbUser && user) { if (!dbUser && user) {
const userService = new UserAccountService(prisma); const userService = new UserAccountService(prisma);
dbUser = await userService.getUserBySupabaseId(user.id); dbUser = await userService.getFullUserBySupabaseId(user.id);
if (!dbUser && user) { if (!dbUser && user) {
dbUser = await userService.createUser( user.id, user.user_metadata.full_name ); dbUser = await userService.createUser( user.id, user.user_metadata.full_name );

View File

@@ -1,6 +1,7 @@
import UserAccountService from '~~/lib/services/user.account.service'; import UserAccountService from '~~/lib/services/user.account.service';
import { protectedProcedure, router } from '../trpc' import { protectedProcedure, router } from '../trpc'
import { ACCOUNT_ACCESS } from '@prisma/client'; import { ACCOUNT_ACCESS } from '@prisma/client';
import { z } from 'zod';
export const userAccountRouter = router({ export const userAccountRouter = router({
getDBUser: protectedProcedure getDBUser: protectedProcedure
@@ -10,10 +11,10 @@ export const userAccountRouter = router({
} }
}), }),
changeAccountPlan: protectedProcedure changeAccountPlan: protectedProcedure
.query(async ({ ctx }) => { .input(z.object({ account_id: z.number(), plan_id: z.number() }))
.query(async ({ ctx, input }) => {
const uaService = new UserAccountService(ctx.prisma); const uaService = new UserAccountService(ctx.prisma);
// TODO - account id and plan should be an input param and then the ternary removed const account = await uaService.changeAccountPlan(input.account_id, input.plan_id);
const account = (ctx.dbUser?.memberships[0].account_id)?await uaService.changeAccountPlan(ctx.dbUser?.memberships[0].account_id, 2):null;
return { return {
account, account,
} }

View File

@@ -1,10 +1,10 @@
import { Membership, Note, User } from ".prisma/client" import { Membership, Note, User } from ".prisma/client"
import { defineStore } from "pinia" import { defineStore } from "pinia"
export type DBUser = User & { memberships: Membership[]; } import { FullDBUser, MembershipWithAccount } from "~~/lib/services/user.account.service"
interface State { interface State {
dbUser?: DBUser dbUser?: FullDBUser
activeMembership: Membership | null activeMembership: MembershipWithAccount | null
notes: Note[] notes: Note[]
} }
@@ -28,9 +28,7 @@ export const useAppStore = defineStore('app', {
} }
}, },
async fetchNotesForCurrentUser() { async fetchNotesForCurrentUser() {
if(!this.activeMembership) { if(!this.activeMembership) { return; }
return;
}
const { $client } = useNuxtApp(); const { $client } = useNuxtApp();
const { data: foundNotes } = await $client.notes.getForCurrentUser.useQuery({account_id: this.activeMembership.account_id}); const { data: foundNotes } = await $client.notes.getForCurrentUser.useQuery({account_id: this.activeMembership.account_id});
@@ -38,11 +36,19 @@ export const useAppStore = defineStore('app', {
this.notes = foundNotes.value.notes; this.notes = foundNotes.value.notes;
} }
}, },
async changeActiveMembership(membership: Membership) { async changeActiveMembership(membership: MembershipWithAccount) {
if(membership !== this.activeMembership){ if(membership !== this.activeMembership){
this.activeMembership = membership; this.activeMembership = membership;
await this.fetchNotesForCurrentUser(); await this.fetchNotesForCurrentUser();
} }
}, },
async changeAccountPlan(plan_id: number){
if(!this.activeMembership) { return; }
const { $client } = useNuxtApp();
const { data: account } = await $client.userAccount.changeAccountPlan.useQuery({account_id: this.activeMembership.account_id, plan_id});
if(account.value?.account){
this.activeMembership.account = account.value.account;
}
}
} }
}); });