Skip to content

Commit

Permalink
Merge branch 'main' into feature/tag-incident-commander
Browse files Browse the repository at this point in the history
  • Loading branch information
GabDug authored Sep 30, 2024
2 parents f070514 + 4d64afa commit 0dfae8e
Show file tree
Hide file tree
Showing 54 changed files with 1,693 additions and 1,512 deletions.
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ repos:
- id: djhtml
# djcss disabled for now
- id: djjs

- repo: https://github.com/adamchainz/djade-pre-commit
rev: "1.1.1"
hooks:
- id: djade
args: [--target-version, "4.2"]
1,952 changes: 991 additions & 961 deletions package-lock.json

Large diffs are not rendered by default.

36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,36 @@
"keywords": [],
"author": "",
"dependencies": {
"@alpinejs/collapse": "^3.13.7",
"@alpinejs/focus": "^3.13.7",
"@alpinejs/morph": "^3.13.7",
"alpinejs": "^3.13.7",
"htmx.org": "^1.9.11",
"@alpinejs/collapse": "^3.14.1",
"@alpinejs/focus": "^3.14.1",
"@alpinejs/morph": "^3.14.1",
"alpinejs": "^3.14.1",
"htmx.org": "^2.0.2",
"hyperscript.org": "^0.9.12"
},
"devDependencies": {
"@babel/core": "^7.24.3",
"@babel/preset-env": "^7.24.3",
"@babel/preset-env": "^7.25.4",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-inject": "^5.0.5",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/line-clamp": "^0.4.4",
"@tailwindcss/typography": "^0.5.10",
"autoprefixer": "^10.4.19",
"clean-css-cli": "^5.6.2",
"@tailwindcss/typography": "^0.5.15",
"autoprefixer": "^10.4.20",
"clean-css-cli": "^5.6.3",
"cross-env": "^7.0.3",
"daisyui": "^4.8.0",
"postcss": "^8.4.38",
"daisyui": "^4.12.10",
"postcss": "^8.4.47",
"postcss-import": "^16.1.0",
"rollup": "^4.13.0",
"rollup": "^4.22.5",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-string": "^3.0.0",
"stylelint": "^16.3.0",
"stylelint-config-standard": "^36.0.0",
"tailwindcss": "^3.4.1"
"stylelint": "^16.9.0",
"stylelint-config-standard": "^36.0.1",
"tailwindcss": "^3.4.13"
}
}
688 changes: 381 additions & 307 deletions pdm.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ dev = [
"django-watchfiles>=0.1.0",
"ruff>=0.1.6",
"mypy-to-codeclimate>=0.0.3",
"djade>=1.1.1",
]
tests = [
"pytest>=7.4.3",
Expand Down Expand Up @@ -542,7 +543,7 @@ external = [
]

[tool.ruff.lint.per-file-ignores]
"**/components/**/*.py" = ["INP"]
"**/components/**/*.py" = ["INP", "TCH002", "TCH003"]
"__init__.py" = ["F401"]
"_version.py" = ["Q000", "UP", "I"]
"**/settings_builder.py" = ["F403"]
Expand Down
4 changes: 2 additions & 2 deletions src/firefighter/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,12 @@ class IncidentSerializer(TaggitSerializer, serializers.ModelSerializer[Incident]
created_by = UserSerializer(read_only=True)
slack_channel_name = serializers.SerializerMethodField()

created_by_email = CreatableSlugRelatedField[User]( # type: ignore[misc]
created_by_email = CreatableSlugRelatedField[User](
source="created_by",
write_only=True,
slug_field="email",
queryset=User.objects.all(),
validators=[EmailValidator()],
validators=[EmailValidator()], # type: ignore[list-item]
)

tags = TagListSerializerField(read_only=True)
Expand Down
31 changes: 24 additions & 7 deletions src/firefighter/components/avatar/avatar.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any
from typing import Any, NotRequired, Required, TypedDict

from django_components import component
from django_components import EmptyTuple, component

if TYPE_CHECKING:
from firefighter.incidents.models.user import User
from firefighter.incidents.models.user import User

logger = logging.getLogger(__name__)


class Data(TypedDict):
user: User
size_tailwind: int
size_px: int


Args = tuple[User]


class Kwargs(TypedDict, total=False):
user: Required[User]
size: NotRequired[str]


@component.register("avatar")
class Avatar(component.Component):
class Avatar(component.Component[EmptyTuple, Kwargs, Data, Any]):
template_name = "avatar/avatar.html"

def get_context_data(self, user: User, *args: Any, **kwargs: Any) -> dict[str, Any]:
def get_context_data(self, user: User, **kwargs: Any) -> Data:
size_px, size_tailwind = (40, 10) if kwargs.get("size") == "md" else (80, 20)
return {"user": user, "size_tailwind": size_tailwind, "size_px": size_px}
return {
"user": user,
"size_tailwind": size_tailwind,
"size_px": size_px,
}
13 changes: 9 additions & 4 deletions src/firefighter/components/card/card.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
from __future__ import annotations

import logging
from typing import Any
from typing import Any, NotRequired, Required, TypedDict, Unpack

from django_components import component
from django_components import EmptyTuple, component

logger = logging.getLogger(__name__)


class Data(TypedDict, total=False):
id: Required[str]
card_title: NotRequired[str]


@component.register("card")
class Card(component.Component):
class Card(component.Component[EmptyTuple, Data, Data, Any]):
template_name = "card/card.html"

def get_context_data(self, *args: Any, **kwargs: dict[str, Any]) -> dict[str, Any]:
def get_context_data(self, *args: Any, **kwargs: Unpack[Data]) -> Data:
return kwargs
33 changes: 26 additions & 7 deletions src/firefighter/components/export_button/export_button.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
from __future__ import annotations

from typing import Any
from collections.abc import Mapping, Sequence
from typing import Any, NotRequired, Required, TypedDict

from django.urls import reverse
from django_components import component
from django_components import EmptyTuple, component


class _Format(TypedDict):
label: str
url: str
fields: str


class Data(TypedDict):
default_format: Mapping[str, Any] # _Format
formats: Sequence[Mapping[str, Any]] # list[_Format]


Args = tuple[str]


class Kwargs(TypedDict, total=False):
base_url: Required[str]
default_fmt: NotRequired[tuple[str, str | None, str | None]]


@component.register("export_button")
class ExportButton(component.Component):
class ExportButton(component.Component[EmptyTuple, Kwargs, Data, Any]):
template_name = "export_button/export_button.html"

def get_context_data(
self,
base_url: str,
*args: Any,
default_fmt: tuple[str, str | None, str | None] | None = None,
**kwargs: Any,
) -> dict[str, Any]:
) -> Data:
default_fmt = default_fmt or ("csv", None, None)
fmts: list[tuple[str, str | None, str | None]] = [
("json", None, None),
Expand All @@ -28,12 +47,12 @@ def get_context_data(
default_format = self.make_fmt(default_fmt, base_url)
formats = [self.make_fmt(fmt, base_url) for fmt in fmts]

return {"default_format": default_format, "formats": formats}
return Data(default_format=default_format, formats=formats)

@staticmethod
def make_fmt(
format_: tuple[str, str | None, str | None], base_reverse: str
) -> dict[str, str]:
) -> _Format:
return {
"label": f"Export {format_[0].upper()}{' ' + format_[2] if format_[2] else ''}",
"url": f"{reverse(base_reverse, args=[format_[0]])}",
Expand Down
27 changes: 15 additions & 12 deletions src/firefighter/components/form/form.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any
from typing import Any, NotRequired, TypedDict

from django_components import component

if TYPE_CHECKING:
from django import forms
from django import forms
from django_components import EmptyTuple, component
from django_components.slots import SlotContent

logger = logging.getLogger(__name__)


class Slots(TypedDict, total=False):
form_footer: NotRequired[SlotContent[Any]]


class Data(TypedDict):
form: forms.Form


@component.register("form")
class Form(component.Component):
class Form(component.Component[EmptyTuple, Data, Data, Slots]): # type: ignore[type-var] # type: ignore[override]
template_name = "form/form.html"

def get_context_data(
self, form: forms.Form, *args: Any, **kwargs: Any
) -> dict[str, Any]:
return {
"form": form,
}
def get_context_data(self, form: forms.Form, **kwargs: Any) -> Data:
return Data(form=form)
30 changes: 19 additions & 11 deletions src/firefighter/components/form_field/form_field.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any
from typing import (
Any,
Never,
TypedDict,
)

from django_components import component

if TYPE_CHECKING:
from django import forms
from django import forms
from django_components import EmptyTuple, component

logger = logging.getLogger(__name__)


class Data(TypedDict):
field: forms.Field | forms.BoundField
field_input_class: str


class Kwargs(TypedDict, total=True):
field: forms.Field | forms.BoundField


@component.register("form_field")
class FormField(component.Component):
class FormField(component.Component[EmptyTuple, Kwargs, Data, Any]):
template_name = "form_field/form_field.html"

def get_context_data(
self, field: forms.BoundField | forms.Field, *args: Any, **kwargs: Any
) -> dict[str, Any]:
if field is None:
raise ValueError("Field not set!")

self, field: forms.BoundField | forms.Field, **kwargs: Never
) -> Data:
input_class = "input-ff"

return {"field": field, "field_input_class": input_class}
15 changes: 10 additions & 5 deletions src/firefighter/components/messages/messages.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
from __future__ import annotations

import logging
from typing import Any
from typing import Any, TypedDict

from django_components import component
from django.contrib.messages.storage.base import BaseStorage
from django_components import EmptyTuple, component

logger = logging.getLogger(__name__)


class Data(TypedDict):
messages: BaseStorage


@component.register("messages")
class Messages(component.Component):
class Messages(component.Component[EmptyTuple, Data, Data, Any]):
template_name = "messages/messages.html"

def get_context_data(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
return kwargs
def get_context_data(self, messages: BaseStorage, **kwargs: Any) -> Data:
return Data(messages=messages)
33 changes: 24 additions & 9 deletions src/firefighter/components/modal/modal.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
from __future__ import annotations

import logging
from typing import Any
from typing import Any, NotRequired, Required, TypedDict, Unpack

from django_components import component
from django.utils.safestring import SafeString
from django_components import EmptyTuple, component
from django_components.slots import SlotFunc

logger = logging.getLogger(__name__)

SlotContent = str | SafeString | SlotFunc[Any]


class Kwargs(TypedDict, total=False):
autoplay: Required[bool]
header: NotRequired[bool]


class Data(TypedDict):
autoplay: bool


class Slots(TypedDict):
modal_header: NotRequired[SlotContent]
modal_content: NotRequired[SlotContent]
modal_enabler: NotRequired[SlotContent]


@component.register("modal")
class Modal(component.Component):
class Modal(component.Component[EmptyTuple, Kwargs, Data, Any]):
template_name = "modal/modal.html"

def get_context_data(
self, *args: Any, autoplay: bool = False, **kwargs: Any
) -> dict[str, bool]:
return {
"autoplay": autoplay,
}
def get_context_data(self, *args: Any, **kwargs: Unpack[Kwargs]) -> Data:
return Kwargs(autoplay=kwargs["autoplay"])
2 changes: 2 additions & 0 deletions src/firefighter/confluence/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ def create_postmortem_for_incident(incident: Incident) -> PostMortem:
class ConfluencePage(models.Model):
"""Represents a Confluence page."""

objects: ClassVar[models.Manager[ConfluencePage]]

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

name = models.CharField(max_length=255)
Expand Down
Loading

0 comments on commit 0dfae8e

Please sign in to comment.