create utility types on server - include Account with Membership - start moving account functions to store actions
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
}}}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user