Skip to content

Commit

Permalink
new: Post a solution
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Nov 23, 2022
2 parents 8c144bf + 47b14e7 commit 679f547
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 24 deletions.
1 change: 1 addition & 0 deletions assets/icons/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ModalController from '@/controllers/modal_controller.js';
import ModalOpenerController from '@/controllers/modal_opener_controller.js';
import PopupController from '@/controllers/popup_controller.js';
import NewTicketController from '@/controllers/new_ticket_controller.js';
import TicketEditorController from '@/controllers/ticket_editor_controller.js';
import TinymceController from '@/controllers/tinymce_controller.js';

const application = Application.start();
Expand All @@ -19,6 +20,7 @@ application.register('form-priority', FormPriorityController);
application.register('modal', ModalController);
application.register('modal-opener', ModalOpenerController);
application.register('new-ticket', NewTicketController);
application.register('ticket-editor', TicketEditorController);
application.register('popup', PopupController);
application.register('tinymce', TinymceController);

Expand Down
30 changes: 30 additions & 0 deletions assets/javascripts/controllers/ticket_editor_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This file is part of Bileto.
// Copyright 2022 Probesys
// SPDX-License-Identifier: AGPL-3.0-or-later

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
static get targets () {
return ['solutionCheckbox', 'statusSelect'];
}

connect () {
this.updateStatus();
}

updateStatus () {
const isSolution = this.solutionCheckboxTarget.checked;

if (isSolution) {
this.statusSelectTarget.value = 'resolved';
for (const option of this.statusSelectTarget.options) {
option.disabled = option.value !== 'resolved';
}
} else {
for (const option of this.statusSelectTarget.options) {
option.disabled = false;
}
}
}
}
23 changes: 22 additions & 1 deletion assets/stylesheets/components/messages.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@
border-radius: 0.5rem;
}

.message--solution .message__box {
border-width: 2px;
border-color: var(--color-success6);
}

.message__box::before {
content: " ";

Expand All @@ -79,6 +84,10 @@
clip-path: polygon(0 50%, 100% 0, 100% 100%);
}

.message--solution .message__box::before {
background-color: var(--color-success6);
}

.message__top {
display: flex;
padding: 1rem 2rem;
Expand All @@ -90,8 +99,16 @@
border-radius: 0.5rem 0.5rem 0 0;
}

.message--solution .message__top {
color: var(--color-success12);

background-color: var(--color-success3);
border-bottom-width: 2px;
border-bottom-color: var(--color-success6);
}

.message__top * + * {
margin-left: 0.25rem;
margin-left: 0.5rem;
}

.message__top-separator {
Expand All @@ -107,6 +124,10 @@
font-size: 0.9em;
}

.message--solution .message__date {
color: var(--color-success11);
}

.message__content {
padding: 2rem;

Expand Down
62 changes: 62 additions & 0 deletions migrations/Version20221123133721MoveSolutionToTicket.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

// This file is part of Bileto.
// Copyright 2022 Probesys
// SPDX-License-Identifier: AGPL-3.0-or-later

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20221123133721MoveSolutionToTicket extends AbstractMigration
{
public function getDescription(): string
{
return 'Drop message.is_solution and replace by ticket.solution';
}

public function up(Schema $schema): void
{
$dbPlatform = $this->connection->getDatabasePlatform()->getName();
if ($dbPlatform === 'postgresql') {
$this->addSql('ALTER TABLE message DROP is_solution');
$this->addSql('ALTER TABLE ticket ADD solution_id INT DEFAULT NULL');
$this->addSql(<<<SQL
ALTER TABLE ticket
ADD CONSTRAINT FK_97A0ADA31C0BE183
FOREIGN KEY (solution_id)
REFERENCES message (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql('CREATE UNIQUE INDEX UNIQ_97A0ADA31C0BE183 ON ticket (solution_id)');
} elseif ($dbPlatform === 'mysql') {
$this->addSql('ALTER TABLE message DROP is_solution');
$this->addSql('ALTER TABLE ticket ADD solution_id INT DEFAULT NULL');
$this->addSql(<<<SQL
ALTER TABLE ticket
ADD CONSTRAINT FK_97A0ADA31C0BE183
FOREIGN KEY (solution_id)
REFERENCES message (id)
SQL);
$this->addSql('CREATE UNIQUE INDEX UNIQ_97A0ADA31C0BE183 ON ticket (solution_id)');
}
}

public function down(Schema $schema): void
{
$dbPlatform = $this->connection->getDatabasePlatform()->getName();
if ($dbPlatform === 'postgresql') {
$this->addSql('ALTER TABLE message ADD is_solution BOOLEAN NOT NULL');
$this->addSql('ALTER TABLE ticket DROP CONSTRAINT FK_97A0ADA31C0BE183');
$this->addSql('DROP INDEX UNIQ_97A0ADA31C0BE183');
$this->addSql('ALTER TABLE ticket DROP solution_id');
} elseif ($dbPlatform === 'mysql') {
$this->addSql('ALTER TABLE ticket DROP FOREIGN KEY FK_97A0ADA31C0BE183');
$this->addSql('DROP INDEX UNIQ_97A0ADA31C0BE183 ON ticket');
$this->addSql('ALTER TABLE ticket DROP solution_id');
$this->addSql('ALTER TABLE message ADD is_solution TINYINT(1) NOT NULL');
}
}
}
2 changes: 1 addition & 1 deletion public/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/Controller/Organizations/TicketsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ public function create(
$message->setCreatedBy($user);
$message->setTicket($ticket);
$message->setIsPrivate(false);
$message->setIsSolution(false);
$message->setVia('webapp');

$errors = $validator->validate($message);
Expand Down
12 changes: 11 additions & 1 deletion src/Controller/Tickets/MessagesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public function create(
$messageContent = $request->request->get('message', '');
$messageContent = $appMessageSanitizer->sanitize($messageContent);

/** @var boolean $isSolution */
$isSolution = $request->request->get('isSolution', false);

/** @var string $status */
$status = $request->request->get('status', '');

Expand All @@ -50,6 +53,7 @@ public function create(
'message' => $messageContent,
'status' => $status,
'statuses' => Ticket::getStatusesWithLabels(),
'isSolution' => $isSolution,
'error' => $this->csrfError(),
]);
}
Expand All @@ -60,7 +64,6 @@ public function create(
$message->setCreatedBy($user);
$message->setTicket($ticket);
$message->setIsPrivate(false);
$message->setIsSolution(false);
$message->setVia('webapp');

$errors = $validator->validate($message);
Expand All @@ -72,10 +75,16 @@ public function create(
'message' => $messageContent,
'status' => $status,
'statuses' => Ticket::getStatusesWithLabels(),
'isSolution' => $isSolution,
'errors' => $this->formatErrors($errors),
]);
}

if ($isSolution) {
$ticket->setSolution($message);
$status = 'resolved';
}

$ticket->setStatus($status);

$errors = $validator->validate($ticket);
Expand All @@ -86,6 +95,7 @@ public function create(
'message' => $messageContent,
'status' => $status,
'statuses' => Ticket::getStatusesWithLabels(),
'isSolution' => $isSolution,
'errors' => $this->formatErrors($errors),
]);
}
Expand Down
1 change: 1 addition & 0 deletions src/Controller/TicketsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function show(Ticket $ticket): Response
'message' => '',
'status' => 'pending',
'statuses' => Ticket::getStatusesWithLabels(),
'isSolution' => false,
]);
}
}
15 changes: 0 additions & 15 deletions src/Entity/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ class Message
#[ORM\Column]
private bool $isPrivate = false;

#[ORM\Column]
private bool $isSolution = false;

#[ORM\Column(length: 32, options: ['default' => self::DEFAULT_VIA])]
#[Assert\Choice(
choices: self::VIAS,
Expand Down Expand Up @@ -94,18 +91,6 @@ public function setIsPrivate(bool $isPrivate): self
return $this;
}

public function isSolution(): ?bool
{
return $this->isSolution;
}

public function setIsSolution(bool $isSolution): self
{
$this->isSolution = $isSolution;

return $this;
}

public function getVia(): ?string
{
return $this->via;
Expand Down
19 changes: 18 additions & 1 deletion src/Entity/Ticket.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class Ticket
#[ORM\OneToMany(mappedBy: 'ticket', targetEntity: Message::class, orphanRemoval: true)]
private Collection $messages;

#[ORM\OneToOne(cascade: ['persist'])]
private ?Message $solution = null;

public function __construct()
{
$this->messages = new ArrayCollection();
Expand Down Expand Up @@ -189,9 +192,11 @@ public function getStatusBadgeColor(): ?string
$this->status === 'assigned' ||
$this->status === 'in_progress'
) {
return 'green';
return 'orange';
} elseif ($this->status === 'pending') {
return 'blue';
} elseif ($this->status === 'resolved') {
return 'green';
} else {
return 'grey';
}
Expand Down Expand Up @@ -364,4 +369,16 @@ public function getMessages(): Collection
{
return $this->messages;
}

public function getSolution(): ?Message
{
return $this->solution;
}

public function setSolution(?Message $solution): self
{
$this->solution = $solution;

return $this;
}
}
27 changes: 25 additions & 2 deletions templates/tickets/show.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<div class="ticket__timeline flow-large">
<div class="flow-large timeline">
{% for message in messages %}
<article class="message">
<article class="message {{ ticket.solution == message ? 'message--solution' }}">
<div class="message__avatar">
{{ icon('circle-user') }}
{% if message.createdBy == ticket.requester %}
Expand All @@ -71,6 +71,12 @@

<div class="message__box">
<div class="message__top">
{% if ticket.solution == message %}
<div class="message__check" title="{{ 'This message resolved the ticket' | trans }}">
{{ icon('check') }}
</div>
{% endif %}

<div class="message__author">
{{ message.createdBy.email }}
</div>
Expand All @@ -93,7 +99,8 @@
<form
action="{{ path('create ticket message', {'uid': ticket.uid}) }}"
method="post"
class="ticket__editor flow"
class="ticket__editor flow-small"
data-controller="ticket-editor"
data-turbo-preserve-scroll
>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('create ticket message') }}">
Expand Down Expand Up @@ -128,6 +135,21 @@
>{{ message }}</textarea>
</div>

<div>
<input
type="checkbox"
id="is-solution"
name="isSolution"
{{ isSolution ? 'checked' }}
data-action="ticket-editor#updateStatus"
data-ticket-editor-target="solutionCheckbox"
/>

<label for="is-solution">
{{ 'Mark as solution' | trans }}
</label>
</div>

<div class="grid grid--cols2">
<div class="form-group--inline">
<label for="status">
Expand All @@ -143,6 +165,7 @@
aria-invalid="true"
aria-errormessage="status-error"
{% endif %}
data-ticket-editor-target="statusSelect"
>
{% for value, label in statuses %}
<option value="{{ value }}" {{ value == status ? 'selected' }}>
Expand Down
2 changes: 1 addition & 1 deletion tests/Controller/Organizations/TicketsControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ public function testPostCreateCreatesATicketAndRedirects(): void
$this->assertSame('medium', $ticket->getUrgency());
$this->assertSame('medium', $ticket->getImpact());
$this->assertSame('medium', $ticket->getPriority());
$this->assertNull($ticket->getSolution());
$this->assertSame($requester->getId(), $ticket->getRequester()->getId());
$this->assertSame($assignee->getId(), $ticket->getAssignee()->getId());
$this->assertSame($organization->getId(), $ticket->getOrganization()->getId());
Expand All @@ -201,7 +202,6 @@ public function testPostCreateCreatesATicketAndRedirects(): void
$this->assertSame($user->getId(), $message->getCreatedBy()->getId());
$this->assertSame($ticket->getId(), $message->getTicket()->getId());
$this->assertFalse($message->isPrivate());
$this->assertFalse($message->isSolution());
$this->assertSame('webapp', $message->getVia());
}

Expand Down
Loading

0 comments on commit 679f547

Please sign in to comment.