Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .github/workflows/ci-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ on:
- "apps/api/**"
- "packages/api-client/**"
- "package.json"
- "package-lock.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- ".github/workflows/ci-api.yml"

jobs:
Expand Down Expand Up @@ -37,13 +38,17 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache: pnpm

- name: Install dependencies
run: npm ci
run: pnpm install --frozen-lockfile

- name: API quality
run: npm run api:ci
run: pnpm run api:ci
13 changes: 9 additions & 4 deletions .github/workflows/ci-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ on:
- "apps/web/**"
- "packages/api-client/**"
- "package.json"
- "package-lock.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- ".github/workflows/ci-web.yml"

jobs:
Expand All @@ -16,13 +17,17 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache: pnpm

- name: Install dependencies
run: npm ci
run: pnpm install --frozen-lockfile

- name: Web quality
run: npm run web:ci
run: pnpm run web:ci
6 changes: 5 additions & 1 deletion apps/api/docs/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1679,7 +1679,7 @@ components:

OrganizationMembership:
type: object
required: [organizationId, userId, role, createdAt]
required: [organizationId, userId, role, createdAt, email]
properties:
organizationId:
type: string
Expand All @@ -1692,6 +1692,10 @@ components:
createdAt:
type: string
format: date-time
email:
type: string
format: email
description: Account email for the member (display label; no separate name field on users).
additionalProperties: false

AddOrganizationMemberRequest:
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/modules/organizations/organizations.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export function toOrganizationResponse(row: Organization): OrganizationResponse
}

export function toOrganizationMembershipResponse(
row: OrganizationMembership,
row: OrganizationMembership & { email: string },
): OrganizationMembershipResponse {
return {
organizationId: row.organizationId,
userId: row.userId,
role: row.role,
createdAt: row.createdAt,
email: row.email,
};
}

Expand Down
13 changes: 11 additions & 2 deletions apps/api/src/modules/organizations/organizations.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
type OrganizationMembership,
organizationMemberships,
organizations,
users,
} from "../../db/schema.js";

export const organizationsRepository = {
Expand Down Expand Up @@ -63,10 +64,18 @@ export const organizationsRepository = {

listMembershipsForOrganization: async (
organizationId: string,
): Promise<OrganizationMembership[]> => {
): Promise<Array<OrganizationMembership & { email: string }>> => {
return db
.select()
.select({
organizationId: organizationMemberships.organizationId,
userId: organizationMemberships.userId,
role: organizationMemberships.role,
createdAt: organizationMemberships.createdAt,
updatedAt: organizationMemberships.updatedAt,
email: users.email,
})
.from(organizationMemberships)
.innerJoin(users, eq(organizationMemberships.userId, users.id))
.where(eq(organizationMemberships.organizationId, organizationId))
.orderBy(
asc(organizationMemberships.createdAt),
Expand Down
5 changes: 4 additions & 1 deletion apps/api/src/modules/organizations/organizations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ export const organizationsService = {
userId: body.userId,
role: body.role,
});
return toOrganizationMembershipResponse(membership);
return toOrganizationMembershipResponse({
...membership,
email: user.email,
});
},

listOrganizationMembers: async (
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/modules/organizations/organizations.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export type OrganizationMembershipResponse = {
userId: string;
role: OrganizationMembershipRole;
createdAt: Date;
/** User's account email (used as display label; users have no separate legal name field). */
email: string;
};

export type AddOrganizationMemberRequest = {
Expand Down
6 changes: 5 additions & 1 deletion apps/web/app/t/[tenantSlug]/(workspace)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Link from "next/link";

type PageProps = { params: Promise<{ tenantSlug: string }> };

export default async function TenantHomePage({ params }: PageProps) {
Expand All @@ -6,7 +8,9 @@ export default async function TenantHomePage({ params }: PageProps) {
<main>
<h1 style={{ fontSize: 22 }}>Workspace</h1>
<p style={{ fontSize: 15, opacity: 0.85 }}>
You are in <strong>{tenantSlug}</strong>. Student and lesson tools arrive in Phase 2 and 3.
You are in <strong>{tenantSlug}</strong>. Manage students from{" "}
<Link href={`/t/${tenantSlug}/students`}>Students</Link>. Lesson tools
arrive in Phase 3.
</p>
</main>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TenantStudentEditPage } from "./tenant-student-edit-page";

export default function StudentEditRoutePage() {
return <TenantStudentEditPage />;
}
Loading
Loading