feat(www): Finish up on the onboarding (#210)

Merging this prematurely to make sure the team is on the same boat... like dang! We need to find a better way to do this. 

Plus it has become too big
This commit is contained in:
Wanjohi
2025-03-26 02:21:53 +03:00
committed by GitHub
parent 957eca7794
commit f62fc1fb4b
106 changed files with 6329 additions and 866 deletions

View File

@@ -2,14 +2,14 @@ import { z } from "zod";
import { Resource } from "sst";
import { bus } from "sst/aws/bus";
import { Common } from "../common";
import { createID, fn } from "../utils";
import { Examples } from "../examples";
import { teamTable } from "./team.sql";
import { createEvent } from "../event";
import { assertActor, withActor } from "../actor";
import { createID, fn } from "../utils";
import { and, eq, sql } from "../drizzle";
import { PlanType, teamTable } from "./team.sql";
import { assertActor, withActor } from "../actor";
import { memberTable } from "../member/member.sql";
import { HTTPException } from 'hono/http-exception';
import { ErrorCodes, VisibleError } from "../error";
import { afterTx, createTransaction, useTransaction } from "../drizzle/transaction";
export module Team {
@@ -19,13 +19,17 @@ export module Team {
description: Common.IdDescription,
example: Examples.Team.id,
}),
slug: z.string().openapi({
slug: z.string().regex(/^[a-z0-9\-]+$/, "Use a URL friendly name.").openapi({
description: "The unique and url-friendly slug of this team",
example: Examples.Team.slug
}),
name: z.string().openapi({
description: "The name of this team",
example: Examples.Team.name
}),
planType: z.enum(PlanType).openapi({
description: "The type of Plan this team is subscribed to",
example: Examples.Team.planType
})
})
.openapi({
@@ -45,40 +49,36 @@ export module Team {
),
};
export class TeamExistsError extends HTTPException {
export class TeamExistsError extends VisibleError {
constructor(slug: string) {
super(
400,
{ message: `There is already a team named "${slug}"`, }
"already_exists",
ErrorCodes.Validation.TEAM_ALREADY_EXISTS,
`There is already a team named "${slug}"`
);
}
}
export const create = fn(
Info.pick({ slug: true, id: true, name: true }).partial({
Info.pick({ slug: true, id: true, name: true, planType: true }).partial({
id: true,
}), (input) => {
createTransaction(async (tx) => {
const id = input.id ?? createID("team");
const result = await tx.insert(teamTable).values({
id,
slug: input.slug,
name: input.name
})
.onConflictDoNothing({ target: teamTable.slug })
if (!result.rowCount) throw new TeamExistsError(input.slug);
await afterTx(() =>
withActor({ type: "system", properties: { teamID: id } }, () =>
bus.publish(Resource.Bus, Events.Created, {
teamID: id,
})
),
);
return id;
}), (input) =>
createTransaction(async (tx) => {
const id = input.id ?? createID("team");
const result = await tx.insert(teamTable).values({
id,
//Remove spaces and make sure it is lowercase (this is just to make sure the frontend did this)
slug: input.slug, //.toLowerCase().replace(/[\s]/g, ''),
planType: input.planType,
name: input.name
})
.onConflictDoNothing({ target: teamTable.slug })
if (result.count === 0) throw new TeamExistsError(input.slug);
return id;
})
)
export const remove = fn(Info.shape.id, (input) =>
useTransaction(async (tx) => {
@@ -147,6 +147,7 @@ export module Team {
id: input.id,
name: input.name,
slug: input.slug,
planType: input.planType,
};
}