Skip to content

Commit 77b89ea

Browse files
authored
feat: switchboard
* fix: improved singleselectfilter design * fix: uiux polish * fix: uiux polish * fix: uiux polish * refactor: big change in backend directories structure - switch to modular * feat(backend): reset, seed, and io (export import) handles * feat(frontend): switchboard aka admin panel boilerplate * feat(frontend): switchboard with reset, seed, import and import functionality * fix: uiux polish * fix: event deletion message * feat(frontend): 404 page * refactor: added useAppConfig composable * style * refactor: recommendations by Sonar
1 parent e84760d commit 77b89ea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1400
-586
lines changed

.env.example

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ DATABASE_URL=postgresql+psycopg2://evsy:evsy@db:5432/evsy
44
FRONTEND_URL=http://localhost:3000
55

66
# Frontend
7-
VITE_ENV=demo # dev | prod | demo
8-
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=demo.evsy.dev
7+
VITE_ENV=dev # dev | prod | demo
98
VITE_API_URL=http://localhost:8000/api/v1
10-
VITE_LOG_LEVEL=error
9+
VITE_LOG_LEVEL=error
10+
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=demo.evsy.dev

backend/app/api/v1/routes/admin.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from fastapi import APIRouter
2+
3+
from app.modules.admin.io.router import router as io_router
4+
from app.modules.admin.reset.router import router as reset_router
5+
from app.modules.admin.seed.router import router as seed_router
6+
7+
router = APIRouter(prefix="/admin")
8+
9+
router.include_router(io_router)
10+
router.include_router(seed_router)
11+
router.include_router(reset_router)
Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
from fastapi import APIRouter, Depends, HTTPException, status
22
from sqlalchemy.orm import Session
33

4-
from app import crud, schemas
5-
from app.database.database import get_db
4+
from app.core.database import get_db
5+
from app.modules.events import crud as event_crud
6+
from app.modules.events.schemas import EventCreate, EventOut
7+
from app.modules.fields.crud import get_fields_by_ids
8+
from app.modules.tags.crud import get_or_create_tags
69

710
router = APIRouter(prefix="/events", tags=["events"])
811

912

1013
@router.post(
1114
"/",
12-
response_model=schemas.EventOut,
15+
response_model=EventOut,
1316
status_code=status.HTTP_201_CREATED,
1417
summary="Create a new event",
1518
description=(
@@ -22,50 +25,49 @@
2225
400: {"description": "One or more fields do not exist"},
2326
},
2427
)
25-
def create_event(event: schemas.EventCreate, db: Session = Depends(get_db)):
26-
db_fields = crud.get_fields_by_ids(db, event.fields)
28+
def create_event_route(event: EventCreate, db: Session = Depends(get_db)):
29+
db_fields = get_fields_by_ids(db, event.fields)
2730

2831
if len(db_fields) != len(event.fields):
2932
raise HTTPException(status_code=400, detail="One or more fields do not exist.")
3033

31-
_ = crud.get_or_create_tags(db, event.tags)
32-
db_event = crud.create_event(db=db, event=event)
34+
get_or_create_tags(db, event.tags)
35+
db_event = event_crud.create_event(db=db, event=event)
3336
return db_event
3437

3538

3639
@router.get(
3740
"/{event_id}",
38-
response_model=schemas.EventOut,
41+
response_model=EventOut,
3942
summary="Get event by ID",
4043
description="Return a single event by its ID. Includes tags and fields.",
4144
responses={
4245
200: {"description": "Event found"},
4346
404: {"description": "Event not found"},
4447
},
4548
)
46-
def get_event(event_id: int, db: Session = Depends(get_db)):
47-
db_event = crud.get_event(db=db, event_id=event_id)
49+
def get_event_route(event_id: int, db: Session = Depends(get_db)):
50+
db_event = event_crud.get_event(db=db, event_id=event_id)
4851
if db_event is None:
4952
raise HTTPException(status_code=404, detail="Event not found")
5053
return db_event
5154

5255

5356
@router.get(
5457
"/",
55-
response_model=list[schemas.EventOut],
58+
response_model=list[EventOut],
5659
response_model_by_alias=False,
5760
summary="List all events",
5861
description="Return a paginated list of all events with their tags and fields.",
5962
responses={200: {"description": "List of events returned"}},
6063
)
61-
def get_events(db: Session = Depends(get_db)):
62-
events = crud.get_events(db=db)
63-
return events
64+
def list_events_route(db: Session = Depends(get_db)):
65+
return event_crud.get_events(db=db)
6466

6567

6668
@router.put(
6769
"/{event_id}",
68-
response_model=schemas.EventOut,
70+
response_model=EventOut,
6971
summary="Update an existing event",
7072
description=(
7173
"Update an existing analytics event. "
@@ -78,33 +80,33 @@ def get_events(db: Session = Depends(get_db)):
7880
404: {"description": "Event not found"},
7981
},
8082
)
81-
def update_event(
82-
event_id: int, event: schemas.EventCreate, db: Session = Depends(get_db)
83+
def update_event_route(
84+
event_id: int, event: EventCreate, db: Session = Depends(get_db)
8385
):
84-
db_fields = crud.get_fields_by_ids(db, event.fields)
86+
db_fields = get_fields_by_ids(db, event.fields)
8587

8688
if len(db_fields) != len(event.fields):
8789
raise HTTPException(status_code=400, detail="One or more fields do not exist.")
8890

89-
_ = crud.get_or_create_tags(db, event.tags)
90-
db_event = crud.update_event(db=db, event_id=event_id, event=event)
91+
get_or_create_tags(db, event.tags)
92+
db_event = event_crud.update_event(db=db, event_id=event_id, event=event)
9193
if db_event is None:
9294
raise HTTPException(status_code=404, detail="Event not found")
9395
return db_event
9496

9597

9698
@router.delete(
9799
"/{event_id}",
98-
response_model=schemas.EventOut,
100+
response_model=EventOut,
99101
summary="Delete an event",
100102
description="Delete an analytics event by ID.",
101103
responses={
102104
200: {"description": "Event deleted"},
103105
404: {"description": "Event not found"},
104106
},
105107
)
106-
def delete_event(event_id: int, db: Session = Depends(get_db)):
107-
db_event = crud.delete_event(db=db, event_id=event_id)
108+
def delete_event_route(event_id: int, db: Session = Depends(get_db)):
109+
db_event = event_crud.delete_event(db=db, event_id=event_id)
108110
if db_event is None:
109111
raise HTTPException(status_code=404, detail="Event not found")
110112
return db_event
Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,82 @@
11
from fastapi import APIRouter, Depends, HTTPException, Query, status
22
from sqlalchemy.orm import Session
33

4-
from app import crud, schemas
5-
from app.database.database import get_db
4+
from app.core.database import get_db
5+
from app.modules.fields import crud as field_crud
6+
from app.modules.fields.schemas import FieldCreate, FieldOut, FieldOutWithEventCount
67

78
router = APIRouter(prefix="/fields", tags=["fields"])
89

910

1011
@router.post(
1112
"/",
12-
response_model=schemas.FieldOut,
13+
response_model=FieldOut,
1314
status_code=status.HTTP_201_CREATED,
1415
summary="Create a field",
1516
description="Create a new field that can be associated with events.",
16-
responses={
17-
201: {"description": "Field created successfully"},
18-
},
1917
)
20-
def create_field(field: schemas.FieldCreate, db: Session = Depends(get_db)):
21-
db_field = crud.create_field(db=db, field=field)
22-
return db_field
18+
def create_field_route(field: FieldCreate, db: Session = Depends(get_db)):
19+
return field_crud.create_field(db=db, field=field)
2320

2421

2522
@router.get(
2623
"/",
27-
response_model=list[schemas.FieldOut],
24+
response_model=list[FieldOut],
2825
summary="List all fields",
2926
description="Return a paginated list of all fields that can be assigned to events.",
30-
responses={
31-
200: {"description": "List of fields returned"},
32-
},
3327
)
34-
def get_fields(db: Session = Depends(get_db)):
35-
fields = crud.get_fields(db=db)
36-
return fields
28+
def list_fields_route(db: Session = Depends(get_db)):
29+
return field_crud.get_fields(db=db)
3730

3831

3932
@router.get(
4033
"/{field_id}",
41-
response_model=schemas.FieldOut | schemas.FieldOutWithEventCount,
34+
response_model=FieldOut | FieldOutWithEventCount,
4235
summary="Get field by ID",
4336
description="Return a single field by its ID.",
44-
responses={
45-
200: {"description": "Field found"},
46-
404: {"description": "Field not found"},
47-
},
37+
responses={404: {"description": "Field not found"}},
4838
)
49-
def get_field(
39+
def get_field_route(
5040
field_id: int,
5141
with_event_count: bool = Query(False),
5242
db: Session = Depends(get_db),
5343
):
54-
db_field = crud.get_field(db=db, field_id=field_id)
44+
db_field = field_crud.get_field(db=db, field_id=field_id)
5545
if db_field is None:
5646
raise HTTPException(status_code=404, detail="Field not found")
5747

5848
if with_event_count:
59-
count = crud.get_field_event_count(db=db, field_id=field_id)
60-
return schemas.FieldOutWithEventCount(**db_field.__dict__, event_count=count)
49+
count = field_crud.get_field_event_count(db=db, field_id=field_id)
50+
return FieldOutWithEventCount(**db_field.__dict__, event_count=count)
6151

6252
return db_field
6353

6454

6555
@router.put(
6656
"/{field_id}",
67-
response_model=schemas.FieldOut,
57+
response_model=FieldOut,
6858
summary="Update a field",
6959
description="Update the name, description, or type of a field.",
70-
responses={
71-
200: {"description": "Field updated"},
72-
404: {"description": "Field not found"},
73-
},
60+
responses={404: {"description": "Field not found"}},
7461
)
75-
def update_field(
76-
field_id: int, field: schemas.FieldCreate, db: Session = Depends(get_db)
62+
def update_field_route(
63+
field_id: int, field: FieldCreate, db: Session = Depends(get_db)
7764
):
78-
db_field = crud.update_field(db=db, field_id=field_id, field=field)
65+
db_field = field_crud.update_field(db=db, field_id=field_id, field=field)
7966
if db_field is None:
8067
raise HTTPException(status_code=404, detail="Field not found")
8168
return db_field
8269

8370

8471
@router.delete(
8572
"/{field_id}",
86-
response_model=schemas.FieldOut,
73+
response_model=FieldOut,
8774
summary="Delete a field",
8875
description="Delete a field by its ID. This will remove the field from all related events.",
89-
responses={
90-
200: {"description": "Field deleted"},
91-
404: {"description": "Field not found"},
92-
},
76+
responses={404: {"description": "Field not found"}},
9377
)
94-
def delete_field(field_id: int, db: Session = Depends(get_db)):
95-
db_field = crud.delete_field(db=db, field_id=field_id)
78+
def delete_field_route(field_id: int, db: Session = Depends(get_db)):
79+
db_field = field_crud.delete_field(db=db, field_id=field_id)
9680
if db_field is None:
9781
raise HTTPException(status_code=404, detail="Field not found")
9882
return db_field

backend/app/api/v1/routes/generic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from fastapi import APIRouter, Depends, Response
22

33
from app.api.deps import get_settings
4-
from app.schemas import FieldType, LinkType
4+
from app.modules.events.schemas import LinkType
5+
from app.modules.fields.schemas import FieldType
56
from app.settings import Settings
67

78
router = APIRouter()

backend/app/api/v1/routes/tags.py

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,71 @@
11
from fastapi import APIRouter, Depends, HTTPException, status
22
from sqlalchemy.orm import Session
33

4-
from app import crud, schemas
5-
from app.database.database import get_db
4+
from app.core.database import get_db
5+
from app.modules.tags import crud as tag_crud
6+
from app.modules.tags.schemas import TagCreate, TagOut
67

78
router = APIRouter(prefix="/tags", tags=["tags"])
89

910

1011
@router.post(
1112
"/",
12-
response_model=schemas.TagOut,
13+
response_model=TagOut,
1314
status_code=status.HTTP_201_CREATED,
1415
summary="Create a tag",
1516
description="Create a tag manually. Typically, tags are created automatically when creating or updating an event.",
16-
responses={
17-
201: {"description": "Tag created successfully"},
18-
},
1917
)
20-
def create_tag(tag: schemas.TagCreate, db: Session = Depends(get_db)):
21-
db_tag = crud.create_tag(db=db, tag=tag)
22-
return db_tag
18+
def create_tag_route(tag: TagCreate, db: Session = Depends(get_db)):
19+
return tag_crud.create_tag(db=db, tag=tag)
2320

2421

2522
@router.get(
2623
"/",
27-
response_model=list[schemas.TagOut],
24+
response_model=list[TagOut],
2825
summary="List all tags",
2926
description="Return a paginated list of all tags available in the system.",
30-
responses={
31-
200: {"description": "List of tags returned"},
32-
},
3327
)
34-
def get_tags(db: Session = Depends(get_db)):
35-
tags = crud.get_tags(db=db)
36-
return tags
28+
def list_tags_route(db: Session = Depends(get_db)):
29+
return tag_crud.get_tags(db=db)
3730

3831

3932
@router.get(
4033
"/{tag_id}",
41-
response_model=schemas.TagOut,
34+
response_model=TagOut,
4235
summary="Get tag by ID",
4336
description="Return a single tag by its unique identifier.",
44-
responses={
45-
200: {"description": "Tag found"},
46-
404: {"description": "Tag not found"},
47-
},
37+
responses={404: {"description": "Tag not found"}},
4838
)
49-
def get_event(tag_id: str, db: Session = Depends(get_db)):
50-
db_tag = crud.get_tag(db=db, tag_id=tag_id)
39+
def get_tag_route(tag_id: str, db: Session = Depends(get_db)):
40+
db_tag = tag_crud.get_tag(db=db, tag_id=tag_id)
5141
if db_tag is None:
5242
raise HTTPException(status_code=404, detail="Tag not found")
5343
return db_tag
5444

5545

5646
@router.put(
5747
"/{tag_id}",
58-
response_model=schemas.TagOut,
48+
response_model=TagOut,
5949
summary="Update a tag",
6050
description="Update the description of an existing tag.",
61-
responses={
62-
200: {"description": "Tag updated"},
63-
404: {"description": "Tag not found"},
64-
},
51+
responses={404: {"description": "Tag not found"}},
6552
)
66-
def update_tag(tag_id: str, tag: schemas.TagCreate, db: Session = Depends(get_db)):
67-
db_tag = crud.update_tag(db=db, tag_id=tag_id, tag=tag)
53+
def update_tag_route(tag_id: str, tag: TagCreate, db: Session = Depends(get_db)):
54+
db_tag = tag_crud.update_tag(db=db, tag_id=tag_id, tag=tag)
6855
if db_tag is None:
6956
raise HTTPException(status_code=404, detail="Tag not found")
7057
return db_tag
7158

7259

7360
@router.delete(
7461
"/{tag_id}",
75-
response_model=schemas.TagOut,
62+
response_model=TagOut,
7663
summary="Delete a tag",
7764
description="Delete a tag by its ID. This will remove the tag from all related events.",
78-
responses={
79-
200: {"description": "Tag deleted"},
80-
404: {"description": "Tag not found"},
81-
},
65+
responses={404: {"description": "Tag not found"}},
8266
)
83-
def delete_tag(tag_id: str, db: Session = Depends(get_db)):
84-
db_tag = crud.delete_tag(db=db, tag_id=tag_id)
67+
def delete_tag_route(tag_id: str, db: Session = Depends(get_db)):
68+
db_tag = tag_crud.delete_tag(db=db, tag_id=tag_id)
8569
if db_tag is None:
8670
raise HTTPException(status_code=404, detail="Tag not found")
8771
return db_tag

0 commit comments

Comments
 (0)