Add user profiles and account setup page

This commit is contained in:
NeilClack
2026-02-03 10:11:00 -06:00
parent 2df81d20e3
commit c34e10159d
19 changed files with 3542 additions and 153 deletions
@@ -1,78 +0,0 @@
-- CreateTable
CREATE TABLE "user" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"emailVerified" BOOLEAN NOT NULL DEFAULT false,
"image" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "session" (
"id" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"token" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"ipAddress" TEXT,
"userAgent" TEXT,
"userId" TEXT NOT NULL,
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "account" (
"id" TEXT NOT NULL,
"accountId" TEXT NOT NULL,
"providerId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"accessToken" TEXT,
"refreshToken" TEXT,
"idToken" TEXT,
"accessTokenExpiresAt" TIMESTAMP(3),
"refreshTokenExpiresAt" TIMESTAMP(3),
"scope" TEXT,
"password" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "verification" (
"id" TEXT NOT NULL,
"identifier" TEXT NOT NULL,
"value" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
-- CreateIndex
CREATE INDEX "session_userId_idx" ON "session"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
-- CreateIndex
CREATE INDEX "account_userId_idx" ON "account"("userId");
-- CreateIndex
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
-- AddForeignKey
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -1,49 +0,0 @@
-- CreateTable
CREATE TABLE "Affirmation" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"content" TEXT NOT NULL,
"solfeggioFrequencyId" INTEGER NOT NULL,
"categoryId" INTEGER NOT NULL,
"price" DOUBLE PRECISION NOT NULL DEFAULT 0.99,
CONSTRAINT "Affirmation_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SolfeggioFrequency" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"value" DOUBLE PRECISION NOT NULL,
CONSTRAINT "SolfeggioFrequency_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AffirmationCategory" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "AffirmationCategory_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Affirmation_name_key" ON "Affirmation"("name");
-- CreateIndex
CREATE UNIQUE INDEX "Affirmation_content_key" ON "Affirmation"("content");
-- CreateIndex
CREATE UNIQUE INDEX "SolfeggioFrequency_name_key" ON "SolfeggioFrequency"("name");
-- CreateIndex
CREATE UNIQUE INDEX "SolfeggioFrequency_value_key" ON "SolfeggioFrequency"("value");
-- CreateIndex
CREATE UNIQUE INDEX "AffirmationCategory_name_key" ON "AffirmationCategory"("name");
-- AddForeignKey
ALTER TABLE "Affirmation" ADD CONSTRAINT "Affirmation_solfeggioFrequencyId_fkey" FOREIGN KEY ("solfeggioFrequencyId") REFERENCES "SolfeggioFrequency"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Affirmation" ADD CONSTRAINT "Affirmation_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "AffirmationCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -1,13 +0,0 @@
-- AlterTable
ALTER TABLE "user" ADD COLUMN "roleId" INTEGER;
-- CreateTable
CREATE TABLE "UserRole" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "UserRole_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "user" ADD CONSTRAINT "user_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "UserRole"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,173 @@
-- CreateTable
CREATE TABLE "user" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"emailVerified" BOOLEAN NOT NULL DEFAULT false,
"image" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"roleId" INTEGER,
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Profile" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"phoneNumber" TEXT NOT NULL,
CONSTRAINT "Profile_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Address" (
"id" TEXT NOT NULL,
"profileId" TEXT NOT NULL,
"street" TEXT NOT NULL,
"city" TEXT NOT NULL,
"state" TEXT NOT NULL,
"postalCode" TEXT NOT NULL,
"country" TEXT NOT NULL,
CONSTRAINT "Address_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "session" (
"id" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"token" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"ipAddress" TEXT,
"userAgent" TEXT,
"userId" TEXT NOT NULL,
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "account" (
"id" TEXT NOT NULL,
"accountId" TEXT NOT NULL,
"providerId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"accessToken" TEXT,
"refreshToken" TEXT,
"idToken" TEXT,
"accessTokenExpiresAt" TIMESTAMP(3),
"refreshTokenExpiresAt" TIMESTAMP(3),
"scope" TEXT,
"password" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "UserRole" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "UserRole_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "verification" (
"id" TEXT NOT NULL,
"identifier" TEXT NOT NULL,
"value" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Affirmation" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"content" TEXT NOT NULL,
"solfeggioFrequencyId" INTEGER NOT NULL,
"categoryId" INTEGER NOT NULL,
"price" DOUBLE PRECISION NOT NULL DEFAULT 0.99,
CONSTRAINT "Affirmation_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SolfeggioFrequency" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"value" DOUBLE PRECISION NOT NULL,
CONSTRAINT "SolfeggioFrequency_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AffirmationCategory" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "AffirmationCategory_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Profile_userId_key" ON "Profile"("userId");
-- CreateIndex
CREATE INDEX "session_userId_idx" ON "session"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
-- CreateIndex
CREATE INDEX "account_userId_idx" ON "account"("userId");
-- CreateIndex
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
-- CreateIndex
CREATE UNIQUE INDEX "Affirmation_name_key" ON "Affirmation"("name");
-- CreateIndex
CREATE UNIQUE INDEX "Affirmation_content_key" ON "Affirmation"("content");
-- CreateIndex
CREATE UNIQUE INDEX "SolfeggioFrequency_name_key" ON "SolfeggioFrequency"("name");
-- CreateIndex
CREATE UNIQUE INDEX "SolfeggioFrequency_value_key" ON "SolfeggioFrequency"("value");
-- CreateIndex
CREATE UNIQUE INDEX "AffirmationCategory_name_key" ON "AffirmationCategory"("name");
-- AddForeignKey
ALTER TABLE "user" ADD CONSTRAINT "user_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "UserRole"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Profile" ADD CONSTRAINT "Profile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Address" ADD CONSTRAINT "Address_profileId_fkey" FOREIGN KEY ("profileId") REFERENCES "Profile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Affirmation" ADD CONSTRAINT "Affirmation_solfeggioFrequencyId_fkey" FOREIGN KEY ("solfeggioFrequencyId") REFERENCES "SolfeggioFrequency"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Affirmation" ADD CONSTRAINT "Affirmation_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "AffirmationCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+2
View File
@@ -31,6 +31,8 @@ model User {
@@map("user")
}
// I chose to create a Profile table here because honestly, I didn't want to waste
// a bunch of time trying to figure out how to customize the default Better-Auth model.
model Profile {
id String @id @default(uuid())
userId String @unique
+10
View File
@@ -22,6 +22,16 @@ export * from './enums.ts';
*
*/
export type User = Prisma.UserModel
/**
* Model Profile
*
*/
export type Profile = Prisma.ProfileModel
/**
* Model Address
*
*/
export type Address = Prisma.AddressModel
/**
* Model Session
*
+10
View File
@@ -44,6 +44,16 @@ export { Prisma }
*
*/
export type User = Prisma.UserModel
/**
* Model Profile
*
*/
export type Profile = Prisma.ProfileModel
/**
* Model Address
*
*/
export type Address = Prisma.AddressModel
/**
* Model Session
*
File diff suppressed because one or more lines are too long
@@ -385,6 +385,8 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
export const ModelName = {
User: 'User',
Profile: 'Profile',
Address: 'Address',
Session: 'Session',
Account: 'Account',
UserRole: 'UserRole',
@@ -407,7 +409,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
omit: GlobalOmitOptions
}
meta: {
modelProps: "user" | "session" | "account" | "userRole" | "verification" | "affirmation" | "solfeggioFrequency" | "affirmationCategory"
modelProps: "user" | "profile" | "address" | "session" | "account" | "userRole" | "verification" | "affirmation" | "solfeggioFrequency" | "affirmationCategory"
txIsolationLevel: TransactionIsolationLevel
}
model: {
@@ -485,6 +487,154 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
}
}
}
Profile: {
payload: Prisma.$ProfilePayload<ExtArgs>
fields: Prisma.ProfileFieldRefs
operations: {
findUnique: {
args: Prisma.ProfileFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload> | null
}
findUniqueOrThrow: {
args: Prisma.ProfileFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>
}
findFirst: {
args: Prisma.ProfileFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload> | null
}
findFirstOrThrow: {
args: Prisma.ProfileFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>
}
findMany: {
args: Prisma.ProfileFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>[]
}
create: {
args: Prisma.ProfileCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>
}
createMany: {
args: Prisma.ProfileCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.ProfileCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>[]
}
delete: {
args: Prisma.ProfileDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>
}
update: {
args: Prisma.ProfileUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>
}
deleteMany: {
args: Prisma.ProfileDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.ProfileUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.ProfileUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>[]
}
upsert: {
args: Prisma.ProfileUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ProfilePayload>
}
aggregate: {
args: Prisma.ProfileAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateProfile>
}
groupBy: {
args: Prisma.ProfileGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.ProfileGroupByOutputType>[]
}
count: {
args: Prisma.ProfileCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.ProfileCountAggregateOutputType> | number
}
}
}
Address: {
payload: Prisma.$AddressPayload<ExtArgs>
fields: Prisma.AddressFieldRefs
operations: {
findUnique: {
args: Prisma.AddressFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload> | null
}
findUniqueOrThrow: {
args: Prisma.AddressFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>
}
findFirst: {
args: Prisma.AddressFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload> | null
}
findFirstOrThrow: {
args: Prisma.AddressFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>
}
findMany: {
args: Prisma.AddressFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>[]
}
create: {
args: Prisma.AddressCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>
}
createMany: {
args: Prisma.AddressCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.AddressCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>[]
}
delete: {
args: Prisma.AddressDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>
}
update: {
args: Prisma.AddressUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>
}
deleteMany: {
args: Prisma.AddressDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.AddressUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.AddressUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>[]
}
upsert: {
args: Prisma.AddressUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$AddressPayload>
}
aggregate: {
args: Prisma.AddressAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateAddress>
}
groupBy: {
args: Prisma.AddressGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AddressGroupByOutputType>[]
}
count: {
args: Prisma.AddressCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AddressCountAggregateOutputType> | number
}
}
}
Session: {
payload: Prisma.$SessionPayload<ExtArgs>
fields: Prisma.SessionFieldRefs
@@ -1056,6 +1206,30 @@ export const UserScalarFieldEnum = {
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
export const ProfileScalarFieldEnum = {
id: 'id',
userId: 'userId',
firstName: 'firstName',
lastName: 'lastName',
phoneNumber: 'phoneNumber'
} as const
export type ProfileScalarFieldEnum = (typeof ProfileScalarFieldEnum)[keyof typeof ProfileScalarFieldEnum]
export const AddressScalarFieldEnum = {
id: 'id',
profileId: 'profileId',
street: 'street',
city: 'city',
state: 'state',
postalCode: 'postalCode',
country: 'country'
} as const
export type AddressScalarFieldEnum = (typeof AddressScalarFieldEnum)[keyof typeof AddressScalarFieldEnum]
export const SessionScalarFieldEnum = {
id: 'id',
expiresAt: 'expiresAt',
@@ -1326,6 +1500,8 @@ export type PrismaClientOptions = ({
}
export type GlobalOmitConfig = {
user?: Prisma.UserOmit
profile?: Prisma.ProfileOmit
address?: Prisma.AddressOmit
session?: Prisma.SessionOmit
account?: Prisma.AccountOmit
userRole?: Prisma.UserRoleOmit
@@ -52,6 +52,8 @@ export const AnyNull = runtime.AnyNull
export const ModelName = {
User: 'User',
Profile: 'Profile',
Address: 'Address',
Session: 'Session',
Account: 'Account',
UserRole: 'UserRole',
@@ -91,6 +93,30 @@ export const UserScalarFieldEnum = {
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
export const ProfileScalarFieldEnum = {
id: 'id',
userId: 'userId',
firstName: 'firstName',
lastName: 'lastName',
phoneNumber: 'phoneNumber'
} as const
export type ProfileScalarFieldEnum = (typeof ProfileScalarFieldEnum)[keyof typeof ProfileScalarFieldEnum]
export const AddressScalarFieldEnum = {
id: 'id',
profileId: 'profileId',
street: 'street',
city: 'city',
state: 'state',
postalCode: 'postalCode',
country: 'country'
} as const
export type AddressScalarFieldEnum = (typeof AddressScalarFieldEnum)[keyof typeof AddressScalarFieldEnum]
export const SessionScalarFieldEnum = {
id: 'id',
expiresAt: 'expiresAt',
+2
View File
@@ -9,6 +9,8 @@
* 🟢 You can import this file directly.
*/
export type * from './models/User.ts'
export type * from './models/Profile.ts'
export type * from './models/Address.ts'
export type * from './models/Session.ts'
export type * from './models/Account.ts'
export type * from './models/UserRole.ts'
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+124
View File
@@ -243,6 +243,7 @@ export type UserWhereInput = {
role?: Prisma.XOR<Prisma.UserRoleNullableScalarRelationFilter, Prisma.UserRoleWhereInput> | null
sessions?: Prisma.SessionListRelationFilter
accounts?: Prisma.AccountListRelationFilter
profile?: Prisma.XOR<Prisma.ProfileNullableScalarRelationFilter, Prisma.ProfileWhereInput> | null
}
export type UserOrderByWithRelationInput = {
@@ -257,6 +258,7 @@ export type UserOrderByWithRelationInput = {
role?: Prisma.UserRoleOrderByWithRelationInput
sessions?: Prisma.SessionOrderByRelationAggregateInput
accounts?: Prisma.AccountOrderByRelationAggregateInput
profile?: Prisma.ProfileOrderByWithRelationInput
}
export type UserWhereUniqueInput = Prisma.AtLeast<{
@@ -274,6 +276,7 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
role?: Prisma.XOR<Prisma.UserRoleNullableScalarRelationFilter, Prisma.UserRoleWhereInput> | null
sessions?: Prisma.SessionListRelationFilter
accounts?: Prisma.AccountListRelationFilter
profile?: Prisma.XOR<Prisma.ProfileNullableScalarRelationFilter, Prisma.ProfileWhereInput> | null
}, "id" | "email">
export type UserOrderByWithAggregationInput = {
@@ -317,6 +320,7 @@ export type UserCreateInput = {
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
}
export type UserUncheckedCreateInput = {
@@ -330,6 +334,7 @@ export type UserUncheckedCreateInput = {
roleId?: number | null
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
}
export type UserUpdateInput = {
@@ -343,6 +348,7 @@ export type UserUpdateInput = {
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
}
export type UserUncheckedUpdateInput = {
@@ -356,6 +362,7 @@ export type UserUncheckedUpdateInput = {
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
}
export type UserCreateManyInput = {
@@ -470,6 +477,20 @@ export type NullableIntFieldUpdateOperationsInput = {
divide?: number
}
export type UserCreateNestedOneWithoutProfileInput = {
create?: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
connectOrCreate?: Prisma.UserCreateOrConnectWithoutProfileInput
connect?: Prisma.UserWhereUniqueInput
}
export type UserUpdateOneRequiredWithoutProfileNestedInput = {
create?: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
connectOrCreate?: Prisma.UserCreateOrConnectWithoutProfileInput
upsert?: Prisma.UserUpsertWithoutProfileInput
connect?: Prisma.UserWhereUniqueInput
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutProfileInput, Prisma.UserUpdateWithoutProfileInput>, Prisma.UserUncheckedUpdateWithoutProfileInput>
}
export type UserCreateNestedOneWithoutSessionsInput = {
create?: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
connectOrCreate?: Prisma.UserCreateOrConnectWithoutSessionsInput
@@ -540,6 +561,74 @@ export type UserUncheckedUpdateManyWithoutRoleNestedInput = {
deleteMany?: Prisma.UserScalarWhereInput | Prisma.UserScalarWhereInput[]
}
export type UserCreateWithoutProfileInput = {
id: string
name: string
email: string
emailVerified?: boolean
image?: string | null
createdAt?: Date | string
updatedAt?: Date | string
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
}
export type UserUncheckedCreateWithoutProfileInput = {
id: string
name: string
email: string
emailVerified?: boolean
image?: string | null
createdAt?: Date | string
updatedAt?: Date | string
roleId?: number | null
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
}
export type UserCreateOrConnectWithoutProfileInput = {
where: Prisma.UserWhereUniqueInput
create: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
}
export type UserUpsertWithoutProfileInput = {
update: Prisma.XOR<Prisma.UserUpdateWithoutProfileInput, Prisma.UserUncheckedUpdateWithoutProfileInput>
create: Prisma.XOR<Prisma.UserCreateWithoutProfileInput, Prisma.UserUncheckedCreateWithoutProfileInput>
where?: Prisma.UserWhereInput
}
export type UserUpdateToOneWithWhereWithoutProfileInput = {
where?: Prisma.UserWhereInput
data: Prisma.XOR<Prisma.UserUpdateWithoutProfileInput, Prisma.UserUncheckedUpdateWithoutProfileInput>
}
export type UserUpdateWithoutProfileInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
email?: Prisma.StringFieldUpdateOperationsInput | string
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutProfileInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
email?: Prisma.StringFieldUpdateOperationsInput | string
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
}
export type UserCreateWithoutSessionsInput = {
id: string
name: string
@@ -550,6 +639,7 @@ export type UserCreateWithoutSessionsInput = {
updatedAt?: Date | string
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
}
export type UserUncheckedCreateWithoutSessionsInput = {
@@ -562,6 +652,7 @@ export type UserUncheckedCreateWithoutSessionsInput = {
updatedAt?: Date | string
roleId?: number | null
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
}
export type UserCreateOrConnectWithoutSessionsInput = {
@@ -590,6 +681,7 @@ export type UserUpdateWithoutSessionsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutSessionsInput = {
@@ -602,6 +694,7 @@ export type UserUncheckedUpdateWithoutSessionsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
}
export type UserCreateWithoutAccountsInput = {
@@ -614,6 +707,7 @@ export type UserCreateWithoutAccountsInput = {
updatedAt?: Date | string
role?: Prisma.UserRoleCreateNestedOneWithoutUsersInput
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
}
export type UserUncheckedCreateWithoutAccountsInput = {
@@ -626,6 +720,7 @@ export type UserUncheckedCreateWithoutAccountsInput = {
updatedAt?: Date | string
roleId?: number | null
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
}
export type UserCreateOrConnectWithoutAccountsInput = {
@@ -654,6 +749,7 @@ export type UserUpdateWithoutAccountsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
role?: Prisma.UserRoleUpdateOneWithoutUsersNestedInput
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutAccountsInput = {
@@ -666,6 +762,7 @@ export type UserUncheckedUpdateWithoutAccountsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
roleId?: Prisma.NullableIntFieldUpdateOperationsInput | number | null
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
}
export type UserCreateWithoutRoleInput = {
@@ -678,6 +775,7 @@ export type UserCreateWithoutRoleInput = {
updatedAt?: Date | string
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileCreateNestedOneWithoutUserInput
}
export type UserUncheckedCreateWithoutRoleInput = {
@@ -690,6 +788,7 @@ export type UserUncheckedCreateWithoutRoleInput = {
updatedAt?: Date | string
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
profile?: Prisma.ProfileUncheckedCreateNestedOneWithoutUserInput
}
export type UserCreateOrConnectWithoutRoleInput = {
@@ -752,6 +851,7 @@ export type UserUpdateWithoutRoleInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUpdateOneWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutRoleInput = {
@@ -764,6 +864,7 @@ export type UserUncheckedUpdateWithoutRoleInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
profile?: Prisma.ProfileUncheckedUpdateOneWithoutUserNestedInput
}
export type UserUncheckedUpdateManyWithoutRoleInput = {
@@ -828,6 +929,7 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
role?: boolean | Prisma.User$roleArgs<ExtArgs>
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
profile?: boolean | Prisma.User$profileArgs<ExtArgs>
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
}, ExtArgs["result"]["user"]>
@@ -871,6 +973,7 @@ export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs =
role?: boolean | Prisma.User$roleArgs<ExtArgs>
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
profile?: boolean | Prisma.User$profileArgs<ExtArgs>
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
}
export type UserIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
@@ -886,6 +989,7 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
role: Prisma.$UserRolePayload<ExtArgs> | null
sessions: Prisma.$SessionPayload<ExtArgs>[]
accounts: Prisma.$AccountPayload<ExtArgs>[]
profile: Prisma.$ProfilePayload<ExtArgs> | null
}
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
@@ -1293,6 +1397,7 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
role<T extends Prisma.User$roleArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$roleArgs<ExtArgs>>): Prisma.Prisma__UserRoleClient<runtime.Types.Result.GetResult<Prisma.$UserRolePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
sessions<T extends Prisma.User$sessionsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$sessionsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$SessionPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
accounts<T extends Prisma.User$accountsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$accountsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AccountPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
profile<T extends Prisma.User$profileArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$profileArgs<ExtArgs>>): Prisma.Prisma__ProfileClient<runtime.Types.Result.GetResult<Prisma.$ProfilePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
@@ -1792,6 +1897,25 @@ export type User$accountsArgs<ExtArgs extends runtime.Types.Extensions.InternalA
distinct?: Prisma.AccountScalarFieldEnum | Prisma.AccountScalarFieldEnum[]
}
/**
* User.profile
*/
export type User$profileArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
/**
* Select specific fields to fetch from the Profile
*/
select?: Prisma.ProfileSelect<ExtArgs> | null
/**
* Omit specific fields from the Profile
*/
omit?: Prisma.ProfileOmit<ExtArgs> | null
/**
* Choose, which related nodes to fetch as well
*/
include?: Prisma.ProfileInclude<ExtArgs> | null
where?: Prisma.ProfileWhereInput
}
/**
* User without action
*/
+45
View File
@@ -0,0 +1,45 @@
/**
* Formats a string of digits into a US phone number format.
* Handles incomplete numbers progressively.
* This function is designed to be used on the client, or
* when passing the phone number back to the client.
*
* @param number - The input string containing digits (and possibly other characters)
* @returns The formatted phone number, progressively formatted as digits are added
*
* @example
* clientFormatPhoneNumber("817") // "(817)"
* clientFormatPhoneNumber("8178") // "(817) 8"
* clientFormatPhoneNumber("8178888888") // "(817) 888-8888"
*/
export function clientFormatPhoneNumber(number: string): string {
const digits = number.replace(/\D/g, '');
if (digits.length === 0) {
return '';
}
if (digits.length <= 3) {
return `(${digits}`;
}
if (digits.length <= 6) {
return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
}
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6, 10)}`;
}
/**
* Strips all non-digit characters from a phone number string.
* This function is designed to be used on the server only.
*
* @param number - The input string containing a formatted phone number
* @returns Only the numeric digits as a string
*
* @example
* serverFormatPhoneNumber("(817) 888-8888") // "8178888888"
*/
export function serverFormatPhoneNumber(number: string): string {
return number.replace(/\D/g, '');
}
@@ -0,0 +1,23 @@
import type { PageServerLoad } from './$types';
import { prisma } from '$lib/db';
import { error, redirect } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ locals }) => {
const user = await prisma.user.findUnique({
where: {
id: locals.user?.id
},
include: {
profile: true
}
});
if (user) {
if (!user?.profile) {
throw redirect(403, '/account/setup');
}
return { user };
} else {
throw error(403, 'not authorized');
}
};
@@ -0,0 +1,5 @@
<script lang="ts">
import type {PageProps} from "./$types";
let {data}: PageProps = $props();
$inspect(data);
</script>
+35 -1
View File
@@ -1,6 +1,7 @@
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
import { error, fail, isRedirect, redirect, type Actions } from '@sveltejs/kit';
import { prisma } from '$lib/db';
import { serverFormatPhoneNumber } from '$lib/utils';
// Tell the page what to do when it loads
export const load: PageServerLoad = async ({ locals }) => {
@@ -15,3 +16,36 @@ export const load: PageServerLoad = async ({ locals }) => {
return { user };
};
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const userID = data.get('userId') as string;
const firstName = data.get('firstName') as string;
const lastName = data.get('lastName') as string;
const phoneNumber = serverFormatPhoneNumber(data.get('phoneNumber') as string);
try {
const profile = await prisma.profile.create({
data: {
userId: userID,
firstName: firstName,
lastName: lastName,
phoneNumber: phoneNumber
}
});
if (profile) {
console.log(`created profile: ${JSON.stringify(profile)}`);
throw redirect(303, '/account');
} else {
return fail(400);
}
} catch (err) {
if (!isRedirect(err)) {
console.log(err);
throw error(400, 'bad request');
}
}
}
};
+51 -9
View File
@@ -1,12 +1,54 @@
<script lang="ts">
import type { PageProps } from "./$types";
let { data }: PageProps = $props();
$inspect(data);
<!--
There should be a form in the middle
rounded corners
floats above the screen slightly
Profile picture in the center at the top, about 1/3-1/2 the width of the form
Their email address, not editable, instead is a regular sized black text (or white in dark mode)
Input field for their Phone Number
Two inputs, side by side, a little margin between them. First and Last name
A submit button
Save the form then redirect them to their dashboard
-->
<script lang="ts">
import Window from '$lib/components/containers/Window.svelte';
import { clientFormatPhoneNumber } from '$lib/utils';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
$inspect(data);
let phoneNumber = $state('');
function handlePhoneInput(event: Event) {
const input = event.target as HTMLInputElement;
phoneNumber = clientFormatPhoneNumber(input.value);
}
</script>
<div>
<h1>
Account Setup
</h1>
</div>
<Window>
<form method="POST" class="border shadow-lg p-6 rounded-xl">
<p class="mx-auto text-center my-4 italic">Please finish setting up your profile</p>
{#if data.user?.image}
<img src={data.user?.image} class="rounded-full w-[8rem] mx-auto" alt="Profile" />
{:else}
<div class="mx-auto aspect-square w-[8rem] rounded-full bg-slate-900"></div>
{/if}
<p class="mx-auto text-center my-4">{data.user?.email}</p>
<div class="flex flex-col">
<input type="text" name="userId" value={data.user?.id} class="hidden"/>
<input type="text" name="firstName" placeholder="First Name" class="my-2" />
<input type="text" name="lastName" placeholder="Last Name" class="my-2" />
<input
type="tel"
name="phoneNumber"
placeholder="Phone Number"
class="my-2"
bind:value={phoneNumber}
oninput={handlePhoneInput}
/>
</div>
<button type="submit" class="p-2 px-4 bg-blue-300 rounded-xl">Submit</button>
</form>
</Window>