feat: review received form submissions in the admin ui

This commit is contained in:
fromme committed 2026-09-24 16:35:18 +02:00
1 parent f073a2014d
commit cb2d64f2cd
19 files changed
+999 -7

No files matched your search

+80 -7
View File
@@ -14,10 +14,13 @@ use Symfony\Component\Uid\Uuid;
* first, other "collect it and let a human read it" forms later. Unrelated to
* App\Entity\Application, which is a teamer applying for an assignment.
*
* Deliberately neither Blameable nor SoftDeletable: nothing in this application creates these
* rows, and the retention rule for job applications asks for real deletion, so this is the one
* entity here that gets an actual DELETE. A purge has to cover "details" and "payload" as
* well - a json column is exactly what a deletion job forgets.
* Deliberately neither Blameable nor SoftDeletable. Nothing here creates a row - TYPO3 does -
* and the only thing staff write are the four follow-up dates, where the interesting fact is
* that the mail went out or the call happened, not who ticked it off; who did is in the audit
* log the mutating controllers write. No SoftDeletable because the retention rule for job
* applications asks for real deletion, so this is the one entity here that gets an actual
* DELETE. A purge has to cover "details" and "payload" as well - a json column is exactly what
* a deletion job forgets.
*
* The typed columns are the mapped core agreed with the TYPO3 side and all nullable: a form is
* configured without a field as often as with it. "details" is everything else the visitor
@@ -86,14 +89,36 @@ class ApplicationFormSubmission implements TimestampableEntityInterface
#[ORM\Column(length: 255, nullable: true)]
private ?string $city = null;
/**
* The follow-up a reviewer works through after a submission arrived: two mails and two
* phone calls, each stamped when it happened and null until it has. Staff state, never part
* of the payload - fromDto() must not touch them.
*
* The mails carry a time because a send time is a machine fact and stays one once the
* notification mail of §6 is automated. A held call does not: the reviewer records it
* afterwards and knows the day, so storing an invented time would only look precise.
*/
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $firstMailSentAt = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $secondMailSentAt = null;
#[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $firstCallHeldAt = null;
#[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $secondCallHeldAt = null;
/**
* Everything the visitor filled in beyond the mapped core, keyed by the German field label.
* Display only: the labels are staff-written and change on a reword, so nothing may query
* this, build a column from it or migrate old rows to a new spelling (§3.3).
*
* Note the labels come back in MySQL's order, not the form's - a json column normalises an
* object by sorting its keys by length. Render it in whatever order suits the reviewer; the
* form's own order is still in $payload, which is read as one blob and never sorted by us.
* The key order is whatever the engine hands back and is not part of what this carries: on
* MariaDB (dev and production) JSON is an alias for LONGTEXT and the document survives
* verbatim, on MySQL 8 an object is normalised and its keys come back sorted by length.
* Render it in whatever order suits the reviewer rather than relying on either.
*
* @var array<string, string|bool|list<string>>
*/
@@ -309,6 +334,54 @@ class ApplicationFormSubmission implements TimestampableEntityInterface
return $this;
}
public function getFirstMailSentAt(): ?\DateTimeImmutable
{
return $this->firstMailSentAt;
}
public function setFirstMailSentAt(?\DateTimeImmutable $firstMailSentAt): static
{
$this->firstMailSentAt = $firstMailSentAt;
return $this;
}
public function getSecondMailSentAt(): ?\DateTimeImmutable
{
return $this->secondMailSentAt;
}
public function setSecondMailSentAt(?\DateTimeImmutable $secondMailSentAt): static
{
$this->secondMailSentAt = $secondMailSentAt;
return $this;
}
public function getFirstCallHeldAt(): ?\DateTimeImmutable
{
return $this->firstCallHeldAt;
}
public function setFirstCallHeldAt(?\DateTimeImmutable $firstCallHeldAt): static
{
$this->firstCallHeldAt = $firstCallHeldAt;
return $this;
}
public function getSecondCallHeldAt(): ?\DateTimeImmutable
{
return $this->secondCallHeldAt;
}
public function setSecondCallHeldAt(?\DateTimeImmutable $secondCallHeldAt): static
{
$this->secondCallHeldAt = $secondCallHeldAt;
return $this;
}
/**
* @return array<string, string|bool|list<string>>
*/