Skip to content

Commit de89840

Browse files
committed
changes
1 parent d03420e commit de89840

24 files changed

+5784
-2272
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,5 @@ badge-service/badges.zip
135135
backend/custom_admin/src/types.ts
136136
backend/schema.graphql
137137
backend/__pypackages__/
138+
backend/custom_admin/.astro/
139+
backend/custom_admin/core.*

backend/api/schema.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
from .association_membership.mutation import AssociationMembershipMutation
2727
from .cms.schema import CMSQuery
2828
from .sponsors.schema import SponsorsMutation
29+
from .visa.queries import VisaQuery
30+
from .visa.mutations import VisaMutation
2931

3032

3133
@strawberry.type
@@ -44,6 +46,7 @@ class Query(
4446
BadgeScannerQuery,
4547
UserQuery,
4648
CMSQuery,
49+
VisaQuery,
4750
):
4851
pass
4952

@@ -64,6 +67,7 @@ class Mutation(
6467
UsersMutations,
6568
AssociationMembershipMutation,
6669
SponsorsMutation,
70+
VisaMutation,
6771
):
6872
pass
6973

backend/api/visa/mutations.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import json
2+
from api.context import Context
3+
from custom_admin.audit import create_change_admin_log_entry
4+
from visa.models import InvitationLetterDocument as InvitationLetterDocumentModel
5+
from api.visa.permissions import CanEditInvitationLetterDocument
6+
from strawberry.tools import create_type
7+
from api.visa.types import InvitationLetterDocument
8+
import strawberry
9+
10+
11+
@strawberry.input
12+
class UpdateInvitationLetterDocumentPageInput:
13+
id: strawberry.ID
14+
title: str
15+
content: str
16+
17+
18+
@strawberry.input
19+
class UpdateInvitationLetterDocumentStructureInput:
20+
header: str
21+
footer: str
22+
pages: list[UpdateInvitationLetterDocumentPageInput]
23+
24+
def to_json(self) -> str:
25+
return json.dumps(self.to_dict())
26+
27+
28+
@strawberry.input
29+
class UpdateInvitationLetterDocumentInput:
30+
id: strawberry.ID
31+
dynamic_document: UpdateInvitationLetterDocumentStructureInput
32+
33+
34+
@strawberry.field(permission_classes=[CanEditInvitationLetterDocument])
35+
def update_invitation_letter_document(
36+
info: strawberry.Info[Context], input: UpdateInvitationLetterDocumentInput
37+
) -> InvitationLetterDocument:
38+
invitation_letter_document = InvitationLetterDocumentModel.objects.get(id=input.id)
39+
invitation_letter_document.dynamic_document = strawberry.asdict(
40+
input.dynamic_document
41+
)
42+
invitation_letter_document.save(update_fields=["dynamic_document"])
43+
44+
create_change_admin_log_entry(
45+
info.context.request.user,
46+
invitation_letter_document,
47+
change_message="Invitation letter document updated",
48+
)
49+
return InvitationLetterDocument.from_model(invitation_letter_document)
50+
51+
52+
VisaMutation = create_type(
53+
"VisaMutation",
54+
(update_invitation_letter_document,),
55+
)

backend/api/visa/permissions.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from api.permissions import IsStaffPermission
2+
from visa.models import InvitationLetterDocument
3+
4+
5+
class CanViewInvitationLetterDocument(IsStaffPermission):
6+
message = "Cannot view invitation letter document"
7+
8+
def has_permission(self, source, info, **kwargs):
9+
if not super().has_permission(source, info, **kwargs):
10+
return False
11+
12+
self.invitation_letter_document = self.get_invitation_letter_document(kwargs)
13+
user = info.context.request.user
14+
return user.has_perm(
15+
"visa.view_invitationletterdocument", self.invitation_letter_document
16+
)
17+
18+
def get_invitation_letter_document(self, kwargs):
19+
if input := kwargs.get("input", None):
20+
id = input.id
21+
else:
22+
id = kwargs.get("id")
23+
24+
return InvitationLetterDocument.objects.filter(id=id).first()
25+
26+
27+
class CanEditInvitationLetterDocument(CanViewInvitationLetterDocument):
28+
message = "Cannot view invitation letter document"
29+
30+
def has_permission(self, source, info, **kwargs):
31+
if not super().has_permission(source, info, **kwargs):
32+
return False
33+
34+
invitation_letter_document = self.invitation_letter_document
35+
user = info.context.request.user
36+
return user.has_perm(
37+
"visa.change_invitationletterdocument", invitation_letter_document
38+
)

backend/api/visa/queries.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from api.visa.permissions import CanViewInvitationLetterDocument
2+
from visa.models import InvitationLetterDocument as InvitationLetterDocumentModel
3+
from api.visa.types import InvitationLetterDocument
4+
import strawberry
5+
from strawberry.tools import create_type
6+
7+
8+
@strawberry.field(permission_classes=[CanViewInvitationLetterDocument])
9+
def invitation_letter_document(id: strawberry.ID) -> InvitationLetterDocument | None:
10+
invitation_letter_document = InvitationLetterDocumentModel.objects.get(id=id)
11+
return InvitationLetterDocument.from_model(invitation_letter_document)
12+
13+
14+
VisaQuery = create_type(
15+
"VisaQuery",
16+
(invitation_letter_document,),
17+
)

backend/api/visa/types.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import strawberry
2+
3+
4+
@strawberry.type
5+
class InvitationLetterDocumentPage:
6+
id: strawberry.ID
7+
title: str
8+
content: str
9+
10+
@classmethod
11+
def from_object(cls, obj: dict):
12+
return cls(
13+
id=obj.get("id"),
14+
title=obj.get("title"),
15+
content=obj.get("content"),
16+
)
17+
18+
19+
@strawberry.type
20+
class InvitationLetterDocumentStructure:
21+
header: str
22+
footer: str
23+
pages: list[InvitationLetterDocumentPage]
24+
25+
@classmethod
26+
def from_object(cls, obj: dict):
27+
return cls(
28+
header=obj.get("header", ""),
29+
footer=obj.get("footer", ""),
30+
pages=[
31+
InvitationLetterDocumentPage.from_object(page)
32+
for page in obj.get("pages", [])
33+
],
34+
)
35+
36+
37+
@strawberry.type
38+
class InvitationLetterDocument:
39+
id: strawberry.ID
40+
dynamic_document: InvitationLetterDocumentStructure
41+
42+
@classmethod
43+
def from_model(cls, instance):
44+
return cls(
45+
id=instance.id,
46+
dynamic_document=InvitationLetterDocumentStructure.from_object(
47+
instance.dynamic_document or {}
48+
),
49+
)

backend/custom_admin/package.json

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,45 @@
1515
"author": "",
1616
"license": "ISC",
1717
"dependencies": {
18-
"@apollo/client": "^3.9.0",
19-
"@astrojs/react": "^3.0.9",
20-
"@astrojs/tailwind": "^5.1.0",
21-
"@types/react": "^18.2.48",
22-
"@types/react-dom": "^18.2.18",
23-
"astro": "^4.2.7",
24-
"clsx": "^2.1.0",
25-
"graphql": "^16.8.1",
26-
"react": "^18.2.0",
18+
"@apollo/client": "^3.12.3",
19+
"@astrojs/react": "^4.1.0",
20+
"@astrojs/tailwind": "^5.1.3",
21+
"@radix-ui/react-alert-dialog": "^1.1.4",
22+
"@radix-ui/react-dialog": "^1.1.3",
23+
"@radix-ui/react-tabs": "^1.1.2",
24+
"@radix-ui/react-toolbar": "^1.1.1",
25+
"@radix-ui/themes": "^3.1.6",
26+
"@tiptap/extension-color": "^2.10.3",
27+
"@tiptap/extension-list-item": "^2.10.3",
28+
"@tiptap/extension-text-align": "^2.10.3",
29+
"@tiptap/extension-text-style": "^2.10.3",
30+
"@tiptap/pm": "^2.10.3",
31+
"@tiptap/react": "^2.10.3",
32+
"@tiptap/starter-kit": "^2.10.3",
33+
"@types/react": "^19.0.1",
34+
"@types/react-dom": "^19.0.2",
35+
"astro": "^5.0.5",
36+
"clsx": "^2.1.1",
37+
"fabric": "^6.5.3",
38+
"graphql": "^16.10.0",
39+
"lucide-react": "^0.468.0",
40+
"pdfjs-dist": "^4.9.155",
41+
"react": "^19.0.0",
2742
"react-dnd": "^16.0.1",
2843
"react-dnd-html5-backend": "^16.0.1",
29-
"react-dom": "^18.2.0",
30-
"tailwindcss": "^3.4.1"
44+
"react-dom": "^19.0.0",
45+
"tailwindcss": "^3.4.16"
3146
},
3247
"devDependencies": {
33-
"@graphql-codegen/cli": "^5.0.0",
48+
"@graphql-codegen/cli": "^5.0.3",
3449
"@graphql-codegen/near-operation-file-preset": "^3.0.0",
35-
"@graphql-codegen/typescript-operations": "^4.0.1",
36-
"@graphql-codegen/typescript-react-apollo": "^4.1.0",
37-
"@parcel/watcher": "^2.4.0",
38-
"concurrently": "^8.2.2",
50+
"@graphql-codegen/typescript-operations": "^4.4.0",
51+
"@graphql-codegen/typescript-react-apollo": "^4.3.2",
52+
"@parcel/watcher": "^2.5.0",
53+
"concurrently": "^9.1.0",
3954
"http-proxy": "^1.18.1",
40-
"nodemon": "^3.0.3",
41-
"prettier": "^3.2.4",
42-
"ws": "^8.17.1"
55+
"nodemon": "^3.1.9",
56+
"prettier": "^3.4.2",
57+
"ws": "^8.18.0"
4358
}
4459
}

0 commit comments

Comments
 (0)