# Organizations

> Multi-tenancy and team collaboration

## 📋 Overview

Organizations enable teams to collaborate on projects with role-based access control. Multiple users can belong to an organization with different permission levels (OWNER, ADMIN, MEMBER).

## 🎯 User Stories

- **As a team lead**, I want to create an organization for my team
- **As an organization owner**, I want to invite team members with appropriate roles
- **As a member**, I want to access all organization projects
- **As an admin**, I want to manage organization settings and members

## 🏗️ Database Schema

**organizations table**:

```typescript
{
  id: string
  name: string
  slug: string       // Unique URL-friendly identifier
  description: string?
  createdAt: Date
  updatedAt: Date
}
```

**organizationMembers table**:

```typescript
{
  id: string
  organizationId: string
  userId: string
  role: 'OWNER' | 'ADMIN' | 'MEMBER'
  createdAt: Date
}
```

**Indexes**:

```sql
CREATE UNIQUE INDEX organizations_slug_key ON organizations(slug);
CREATE INDEX organization_members_org_user_idx ON organization_members(organization_id, user_id);
CREATE UNIQUE INDEX organization_members_organization_id_user_id_key 
  ON organization_members(organization_id, user_id);
```

## 🔌 API Reference

### Create Organization

```typescript
const org = await trpc.organizations.create.mutate({
  name: 'Acme Corp',
  slug: 'acme-corp',
  description: 'Our company organization'
})

// Response
{
  organization: {
    id: 'org_123',
    name: 'Acme Corp',
    slug: 'acme-corp',
    description: 'Our company organization',
    createdAt: '2024-01-01T00:00:00Z'
  }
}
```

**Validation**:

- Name required (min 1 character)
- Slug must be unique and URL-safe
- Automatically adds creator as OWNER

### List Organizations

```typescript
// Get user's organizations
const orgs = await trpc.organizations.list.query({
  userId: 'user_123'
})

// Response
{
  organizations: [
    {
      id: 'org_123',
      name: 'Acme Corp',
      slug: 'acme-corp',
      role: 'OWNER',
      memberCount: 5,
      projectCount: 12
    }
  ]
}
```

### Add Member

```typescript
await trpc.organizations.addMember.mutate({
  organizationId: 'org_123',
  userId: 'user_456',
  role: 'MEMBER'
})
```

**Permissions**:

- Only OWNER and ADMIN can add members
- Cannot add duplicate members
- Role options: OWNER, ADMIN, MEMBER

### Update Member Role

```typescript
await trpc.organizations.updateMemberRole.mutate({
  organizationId: 'org_123',
  userId: 'user_456',
  role: 'ADMIN'
})
```

**Rules**:

- Only OWNER can change roles
- Cannot remove last OWNER
- Cannot change own role (prevents lockout)

### Remove Member

```typescript
await trpc.organizations.removeMember.mutate({
  organizationId: 'org_123',
  userId: 'user_456'
})
```

**Rules**:

- OWNER and ADMIN can remove members
- Cannot remove last OWNER
- Removes access to all organization projects

## 🎨 Frontend Implementation

### Organization Selector

```tsx
export function OrganizationSelector() {
  const { data } = trpc.organizations.list.useQuery()
  
  return (
    <Select>
      <SelectTrigger>
        <SelectValue placeholder="Select organization" />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="personal">Personal</SelectItem>
        {data?.organizations.map((org) => (
          <SelectItem key={org.id} value={org.id}>
            {org.name}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  )
}
```

### Organization Settings Page

```tsx
export function OrganizationSettings({ orgId }: Props) {
  return (
    <Tabs defaultValue="general">
      <TabsList>
        <TabsTrigger value="general">General</TabsTrigger>
        <TabsTrigger value="members">Members</TabsTrigger>
        <TabsTrigger value="projects">Projects</TabsTrigger>
      </TabsList>

      <TabsContent value="general">
        <OrganizationGeneralSettings orgId={orgId} />
      </TabsContent>

      <TabsContent value="members">
        <MemberManagement orgId={orgId} />
      </TabsContent>

      <TabsContent value="projects">
        <OrganizationProjects orgId={orgId} />
      </TabsContent>
    </Tabs>
  )
}
```

### Member Management

```tsx
export function MemberManagement({ orgId }: Props) {
  const { data } = trpc.organizations.listMembers.useQuery({ orgId })
  const removeMember = trpc.organizations.removeMember.useMutation()

  return (
    <Card>
      <CardHeader>
        <CardTitle>Members</CardTitle>
      </CardHeader>
      <CardContent>
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Name</TableHead>
              <TableHead>Email</TableHead>
              <TableHead>Role</TableHead>
              <TableHead>Actions</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {data?.members.map((member) => (
              <TableRow key={member.id}>
                <TableCell>{member.name}</TableCell>
                <TableCell>{member.email}</TableCell>
                <TableCell>
                  <Badge>{member.role}</Badge>
                </TableCell>
                <TableCell>
                  <DropdownMenu>
                    <DropdownMenuTrigger asChild>
                      <Button variant="ghost" size="sm">
                        <MoreVerticalIcon />
                      </Button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent>
                      <DropdownMenuItem>Change Role</DropdownMenuItem>
                      <DropdownMenuItem
                        onClick={() => removeMember.mutate({
                          orgId,
                          userId: member.id
                        })}
                        className="text-destructive"
                      >
                        Remove
                      </DropdownMenuItem>
                    </DropdownMenuContent>
                  </DropdownMenu>
                </TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </CardContent>
    </Card>
  )
}
```

## 🔐 Permission System

### Role Capabilities

| Action | OWNER | ADMIN | MEMBER |
|--------|-------|-------|--------|
| View projects | ✅ | ✅ | ✅ |
| Create projects | ✅ | ✅ | ✅ |
| Delete projects | ✅ | ✅ | ❌ |
| Add members | ✅ | ✅ | ❌ |
| Remove members | ✅ | ✅ | ❌ |
| Change roles | ✅ | ❌ | ❌ |
| Delete organization | ✅ | ❌ | ❌ |

### Permission Checks

```typescript
function canManageMembers(userRole: string): boolean {
  return ['OWNER', 'ADMIN'].includes(userRole)
}

function canDeleteProject(userRole: string): boolean {
  return ['OWNER', 'ADMIN'].includes(userRole)
}
```

## ✅ Testing

```typescript
describe('Organizations', () => {
  it('creates organization with creator as owner', async () => {
    const org = await trpc.organizations.create.mutate({
      name: 'Test Org',
      slug: 'test-org'
    })
    
    const members = await trpc.organizations.listMembers.query({
      orgId: org.id
    })
    
    expect(members[0].role).toBe('OWNER')
  })
})
```

## 🔗 Related Documentation

- **Projects**: [Assign projects to organizations](./projects.md)
- **GitHub Integration**: [Organization-level OAuth apps](./github-integration.md)
- **API Reference**: [Organizations API](../api-reference/trpc-routers.md)

---

*Next: [GitHub Integration](./github-integration.md) →*
