Skip to content

Commit 3f13345

Browse files
Added page for managing an organization
1 parent 747352b commit 3f13345

File tree

3 files changed

+115
-8
lines changed

3 files changed

+115
-8
lines changed

main.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from fastapi.exceptions import RequestValidationError, HTTPException, StarletteHTTPException
99
from sqlmodel import Session
1010
from routers import authentication, organization, role, user
11-
from utils.auth import get_authenticated_user, get_user_with_relations, get_optional_user, NeedsNewTokens, get_user_from_reset_token, PasswordValidationError, AuthenticationError
12-
from utils.models import User
11+
from utils.auth import get_user_with_relations, get_optional_user, NeedsNewTokens, get_user_from_reset_token, PasswordValidationError, AuthenticationError
12+
from utils.models import User, Organization
1313
from utils.db import get_session, set_up_db
1414

1515
logger = logging.getLogger("uvicorn.error")
@@ -19,8 +19,7 @@
1919
@asynccontextmanager
2020
async def lifespan(app: FastAPI):
2121
# Optional startup logic
22-
# TODO: Set drop=False in production
23-
set_up_db(drop=True)
22+
set_up_db()
2423
yield
2524
# Optional shutdown logic
2625

@@ -250,6 +249,24 @@ async def read_profile(
250249
return templates.TemplateResponse(params["request"], "users/profile.html", params)
251250

252251

252+
@app.get("/organizations/{org_id}")
253+
async def read_organization(
254+
org_id: int,
255+
params: dict = Depends(common_authenticated_parameters)
256+
):
257+
# Get the organization only if the user is a member of it
258+
organization: Organization = params["user"].organizations.get(org_id)
259+
if not organization:
260+
raise organization.OrganizationNotFoundError()
261+
262+
# Eagerly load roles and users
263+
organization.roles
264+
organization.users
265+
params["organization"] = organization
266+
267+
return templates.TemplateResponse(params["request"], "users/organization.html", params)
268+
269+
253270
# -- Include Routers --
254271

255272

routers/organization.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from logging import getLogger
22
from fastapi import APIRouter, Depends, HTTPException, Form
3-
from fastapi.responses import RedirectResponse
3+
from fastapi.responses import RedirectResponse, HTMLResponse
44
from pydantic import BaseModel, ConfigDict, field_validator
55
from sqlmodel import Session, select
66
from utils.db import get_session
@@ -93,7 +93,7 @@ async def as_form(cls, id: int = Form(...), name: str = Form(...)):
9393

9494
# -- Routes --
9595

96-
@router.post("/create", name="create_organization", response_class=RedirectResponse)
96+
@router.post("/create", response_class=RedirectResponse)
9797
def create_organization(
9898
org: OrganizationCreate = Depends(OrganizationCreate.as_form),
9999
user: User = Depends(get_authenticated_user),
@@ -129,7 +129,7 @@ def create_organization(
129129
session.commit()
130130
session.refresh(db_org)
131131

132-
return RedirectResponse(url=f"/profile", status_code=303)
132+
return RedirectResponse(url=f"/organizations/{db_org.id}", status_code=303)
133133

134134

135135
@router.post("/update/{org_id}", name="update_organization", response_class=RedirectResponse)
@@ -162,7 +162,7 @@ def update_organization(
162162
return RedirectResponse(url=f"/profile", status_code=303)
163163

164164

165-
@router.post("/delete/{org_id}", name="delete_organization", response_class=RedirectResponse)
165+
@router.post("/delete/{org_id}", response_class=RedirectResponse)
166166
def delete_organization(
167167
org_id: int,
168168
user: User = Depends(get_user_with_relations),

templates/users/organization.html

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{% extends "base.html" %}
2+
{% from 'components/silhouette.html' import render_silhouette %}
3+
4+
{% block title %}{{ organization.name }}{% endblock %}
5+
6+
{% block content %}
7+
<div class="container mt-5">
8+
<h1 class="mb-4">{{ organization.name }}</h1>
9+
10+
<!-- Organization Roles -->
11+
<div class="card mb-4">
12+
<div class="card-header d-flex justify-content-between align-items-center">
13+
<span>Roles</span>
14+
</div>
15+
<div class="card-body">
16+
<div class="table-responsive">
17+
<table class="table table-hover">
18+
<thead>
19+
<tr>
20+
<th>Role Name</th>
21+
<th>Members</th>
22+
<th>Permissions</th>
23+
</tr>
24+
</thead>
25+
<tbody>
26+
{% for role in organization.roles %}
27+
<tr>
28+
<td>{{ role.name }}</td>
29+
<td>{{ role.users|length }}</td>
30+
<td>
31+
<ul class="list-unstyled mb-0">
32+
{% for permission in role.permissions %}
33+
<li><small>{{ permission.name.value }}</small></li>
34+
{% endfor %}
35+
</ul>
36+
</td>
37+
</tr>
38+
{% endfor %}
39+
</tbody>
40+
</table>
41+
</div>
42+
</div>
43+
</div>
44+
45+
<!-- Organization Members -->
46+
<div class="card mb-4">
47+
<div class="card-header d-flex justify-content-between align-items-center">
48+
<span>Members</span>
49+
</div>
50+
<div class="card-body">
51+
<div class="table-responsive">
52+
<table class="table table-hover">
53+
<thead>
54+
<tr>
55+
<th></th>
56+
<th>Name</th>
57+
<th>Email</th>
58+
<th>Roles</th>
59+
</tr>
60+
</thead>
61+
<tbody>
62+
{% for role in organization.roles %}
63+
{% for user in role.users %}
64+
<tr>
65+
<td class="text-center" style="width: 50px;">
66+
{% if user.avatar_url %}
67+
<img src="{{ user.avatar_url }}" alt="User Avatar" class="rounded-circle" width="40" height="40">
68+
{% else %}
69+
{{ render_silhouette(width=40, height=40) }}
70+
{% endif %}
71+
</td>
72+
<td>{{ user.name }}</td>
73+
<td>{{ user.email }}</td>
74+
<td>
75+
{% for user_role in user.roles %}
76+
{% if user_role.organization_id == organization.id %}
77+
<span class="badge bg-secondary">{{ user_role.name }}</span>
78+
{% endif %}
79+
{% endfor %}
80+
</td>
81+
</tr>
82+
{% endfor %}
83+
{% endfor %}
84+
</tbody>
85+
</table>
86+
</div>
87+
</div>
88+
</div>
89+
</div>
90+
{% endblock %}

0 commit comments

Comments
 (0)