chore: ai, public course and other ui improvements

This commit is contained in:
Rotimi Best
2026-05-09 06:05:03 +01:00
parent 5717dd8004
commit c31ad63955
318 changed files with 20668 additions and 4788 deletions
+174 -2
View File
@@ -2,12 +2,13 @@ import type {
TNewOrganizationInvite,
TNewOrganizationInviteAudit,
TOrganizationInvite,
TOrganizationInviteAudit
TOrganizationInviteAudit,
TNewOrganizationmember
} from '@db/types';
import * as schema from '@db/schema';
import { and, desc, eq, gt, inArray, isNull, sql } from 'drizzle-orm';
import { db } from '@db/drizzle';
import { db, type DbOrTxClient } from '@db/drizzle';
export async function createOrganizationInvite(values: TNewOrganizationInvite): Promise<TOrganizationInvite> {
try {
@@ -358,3 +359,174 @@ export async function getLatestOrganizationInviteRowByOrgAndEmail(
);
}
}
export type TOrganizationInviteAcceptRow = {
invite: TOrganizationInvite;
organization: {
id: string;
name: string;
siteName: string;
};
};
export async function selectOrganizationInviteWithOrgByTokenHash(
dbClient: DbOrTxClient,
tokenHash: string
): Promise<TOrganizationInviteAcceptRow | null> {
try {
const [row] = await dbClient
.select({
invite: schema.organizationInvite,
organization: {
id: schema.organization.id,
name: schema.organization.name,
siteName: schema.organization.siteName
}
})
.from(schema.organizationInvite)
.innerJoin(schema.organization, eq(schema.organizationInvite.organizationId, schema.organization.id))
.where(eq(schema.organizationInvite.tokenHash, tokenHash))
.limit(1);
if (!row) {
return null;
}
return {
...row,
organization: { ...row.organization, siteName: row.organization.siteName ?? '' }
};
} catch (error) {
console.error('selectOrganizationInviteWithOrgByTokenHash error:', error);
throw new Error(`Failed to load organization invite: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
export async function selectOrganizationInviteWithOrgByInviteId(
dbClient: DbOrTxClient,
inviteId: string
): Promise<TOrganizationInviteAcceptRow | null> {
try {
const [row] = await dbClient
.select({
invite: schema.organizationInvite,
organization: {
id: schema.organization.id,
name: schema.organization.name,
siteName: schema.organization.siteName
}
})
.from(schema.organizationInvite)
.innerJoin(schema.organization, eq(schema.organizationInvite.organizationId, schema.organization.id))
.where(eq(schema.organizationInvite.id, inviteId))
.limit(1);
if (!row) {
return null;
}
return {
...row,
organization: { ...row.organization, siteName: row.organization.siteName ?? '' }
};
} catch (error) {
console.error('selectOrganizationInviteWithOrgByInviteId error:', error);
throw new Error(`Failed to load organization invite: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
export async function selectOrganizationMemberByOrgAndNormalizedEmail(
dbClient: DbOrTxClient,
organizationId: string,
normalizedEmail: string
) {
try {
const [row] = await dbClient
.select()
.from(schema.organizationmember)
.where(
and(
eq(schema.organizationmember.organizationId, organizationId),
eq(schema.organizationmember.email, normalizedEmail)
)
)
.limit(1);
return row ?? null;
} catch (error) {
console.error('selectOrganizationMemberByOrgAndNormalizedEmail error:', error);
throw new Error(`Failed to load organization member: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
export async function selectOrganizationMemberByOrgAndProfile(
dbClient: DbOrTxClient,
organizationId: string,
profileId: string
) {
try {
const [row] = await dbClient
.select()
.from(schema.organizationmember)
.where(
and(
eq(schema.organizationmember.organizationId, organizationId),
eq(schema.organizationmember.profileId, profileId)
)
)
.limit(1);
return row ?? null;
} catch (error) {
console.error('selectOrganizationMemberByOrgAndProfile error:', error);
throw new Error(`Failed to load organization member: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
export async function updateOrganizationMemberById(
dbClient: DbOrTxClient,
memberId: number,
data: Partial<TNewOrganizationmember>
) {
try {
await dbClient.update(schema.organizationmember).set(data).where(eq(schema.organizationmember.id, memberId));
} catch (error) {
console.error('updateOrganizationMemberById error:', error);
throw new Error(
`Failed to update organization member: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
export async function claimPendingOrganizationInvite(
dbClient: DbOrTxClient,
inviteId: string,
acceptedByProfileId: string
): Promise<{ id: string } | undefined> {
try {
const [acceptedInvite] = await dbClient
.update(schema.organizationInvite)
.set({
acceptedAt: new Date().toISOString(),
acceptedByProfileId,
updatedAt: new Date().toISOString()
})
.where(
and(
eq(schema.organizationInvite.id, inviteId),
eq(schema.organizationInvite.isRevoked, false),
isNull(schema.organizationInvite.acceptedAt)
)
)
.returning({
id: schema.organizationInvite.id
});
return acceptedInvite;
} catch (error) {
console.error('claimPendingOrganizationInvite error:', error);
throw new Error(
`Failed to finalize organization invite: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
@@ -95,6 +95,32 @@ export const createOrganizationMember = async (data: TNewOrganizationmember, dbC
return member;
};
export async function getOrganizationMemberIdByOrgAndProfile(
organizationId: string,
profileId: string,
dbClient: DbOrTxClient = db
): Promise<number | null> {
try {
const [row] = await dbClient
.select({ id: schema.organizationmember.id })
.from(schema.organizationmember)
.where(
and(
eq(schema.organizationmember.organizationId, organizationId),
eq(schema.organizationmember.profileId, profileId)
)
)
.limit(1);
return row?.id ?? null;
} catch (error) {
console.error('getOrganizationMemberIdByOrgAndProfile error:', error);
throw new Error(
`Failed to resolve organization membership: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Creates multiple organization members in a single query
* @param data Array of organization member creation data
@@ -106,6 +132,24 @@ export const createOrganizationMembers = async (data: TNewOrganizationmember[])
return members;
};
export async function insertOrganizationMembersOnConflictDoNothing(
data: TNewOrganizationmember[],
dbClient: DbOrTxClient = db
): Promise<void> {
if (data.length === 0) {
return;
}
try {
await dbClient.insert(schema.organizationmember).values(data).onConflictDoNothing();
} catch (error) {
console.error('insertOrganizationMembersOnConflictDoNothing error:', error);
throw new Error(
`Failed to insert organization members: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
export const checkSiteNameExists = async (siteName: string): Promise<boolean> => {
const result = await db
.select({ id: schema.organization.id })