Skip to content

Commit

Permalink
feat: tickets (#267)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreiio authored Aug 18, 2023
1 parent a240843 commit 9d5b28b
Show file tree
Hide file tree
Showing 51 changed files with 2,468 additions and 756 deletions.
16 changes: 16 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"trailingComma": "es5",
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"overrides": [
{
"files": ["*.yml", "*.yaml"],
"options": {
"tabWidth": 2
}
}
]
}
27 changes: 27 additions & 0 deletions app/Events/TicketCreated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\Ticket;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class TicketCreated
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public Ticket $ticket;

/**
* Create a new event instance.
*/
public function __construct(Ticket $ticket)
{
$this->ticket = $ticket;
}
}
27 changes: 27 additions & 0 deletions app/Events/TicketReplyReceived.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\TicketMessage;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class TicketReplyReceived
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public TicketMessage $message;

/**
* Create a new event instance.
*/
public function __construct(TicketMessage $message)
{
$this->message = $message;
}
}
27 changes: 27 additions & 0 deletions app/Events/TicketUpdated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\Ticket;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class TicketUpdated
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public Ticket $ticket;

/**
* Create a new event instance.
*/
public function __construct(Ticket $ticket)
{
$this->ticket = $ticket;
}
}
155 changes: 155 additions & 0 deletions app/Filament/Form/Components/Value.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php

declare(strict_types=1);

namespace App\Filament\Forms\Components;

use BackedEnum;
use Carbon\Carbon;
use Closure;
use Filament\Forms\Components\Component;
use Filament\Forms\Components\Concerns;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
use Illuminate\Support\Stringable;

class Value extends Component
{
use Concerns\HasHelperText;
use Concerns\HasHint;
use Concerns\HasName;

protected string $view = 'components.forms.value';

protected bool $empty = false;

protected string | Htmlable | Closure | null $content = null;

protected string | Htmlable | Closure | null $fallback = null;

protected $withTime = false;

protected array $boolean = [];

final public function __construct(string $name)
{
$this->name($name);
$this->statePath($name);
}

public static function make(string $name): static
{
$static = app(static::class, ['name' => $name]);
$static->configure();

return $static;
}

public function empty(): static
{
$this->empty = true;

return $this;
}

public function boolean(string | Htmlable | null $trueLabel = null, string | Htmlable | null $falseLabel = null): static
{
$this->boolean = [
1 => $trueLabel ?? __('forms::components.select.boolean.true'),
0 => $falseLabel ?? __('forms::components.select.boolean.false'),
];

return $this;
}

public function fallback(string | Htmlable | Closure | null $fallback): static
{
$this->fallback = $fallback;

return $this;
}

public function getFallback(): string | Htmlable | null
{
return $this->evaluate($this->fallback);
}

public function content(string | Htmlable | Closure | null $content): static
{
$this->content = $content;

return $this;
}

public function getContent(): string | Htmlable | null
{
if ($this->empty) {
return null;
}

$content = $this->evaluate($this->content) ?? data_get($this->getRecord(), $this->getName());

if (! empty($this->boolean)) {
$content = $this->boolean[\intval(\boolval($content))];
}

$content = match (true) {
$content instanceof BackedEnum => $this->getEnumLabel($content),
$content instanceof Carbon => $this->getFormattedDate($content),
$content instanceof Collection => $this->getFormattedCollection($content),
default => $content,
};

if (! $content instanceof HtmlString) {
$content = Str::of($content)
->trim()
->toHtmlString();
}

if (! $content->isEmpty()) {
return $content;
}

if (null !== $fallback = $this->getFallback()) {
return new HtmlString('<div class="italic">' . $fallback . '</div>');
}

return new HtmlString('<span class="text-gray-500">&mdash;</span>');
}

public function withTime(bool $condition = true): static
{
$this->withTime = $condition;

return $this;
}

protected function getFormattedCollection(Collection $collection, string $glue = ', '): string
{
return $collection
->map(fn ($item) => match (true) {
$item instanceof Htmlable => $item->toHtml(),
$item instanceof Stringable => $item->toString(),
default => $item
})
->join($glue);
}

protected function getFormattedDate(Carbon $date): string
{
return $this->withTime
? $date->toFormattedDateTime()
: $date->toFormattedDate();
}

protected function getEnumLabel(BackedEnum $content): ?string
{
if (! \in_array(\App\Concerns\Enums\Arrayable::class, class_uses_recursive($content))) {
return null;
}

return $content?->label();
}
}
68 changes: 47 additions & 21 deletions app/Filament/Resources/TicketResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Filament\Resources;

use App\Filament\Forms\Components\Value;
use App\Filament\Resources\TicketResource\Pages;
use App\Filament\Resources\TicketResource\RelationManagers\MessagesRelationManager;
use App\Filament\Resources\TicketResource\Widgets\ClosedTicketsWidget;
use App\Filament\Resources\TicketResource\Widgets\OpenTicketsWidget;
use App\Models\Ticket;
use Filament\Resources\Form;
use Filament\Resources\Resource;
use Filament\Resources\Table;
use Filament\Tables;
use Illuminate\Support\HtmlString;

class TicketResource extends Resource
{
Expand All @@ -19,37 +22,60 @@ class TicketResource extends Resource

protected static ?int $navigationSort = 6;

protected static ?string $navigationIcon = 'heroicon-o-collection';
protected static ?string $navigationIcon = 'heroicon-o-mail';

protected static ?string $recordTitleAttribute = 'subject';

public static function form(Form $form): Form
{
return $form
->schema([
//
Value::make('created_at')
->label(__('ticket.date'))
->withTime()
->inlineLabel()
->columnSpanFull(),

Value::make('closed_at')
->label(__('ticket.status'))
->boolean(
trueLabel: new HtmlString('<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-success-50 text-success-700 ring-1 ring-inset ring-success-600/20">' . __('ticket.status.closed') . '</span>'),
falseLabel: new HtmlString('<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-warning-50 text-warning-700 ring-1 ring-inset ring-warning-600/20">' . __('ticket.status.open') . '</span>'),
)
->inlineLabel()
->columnSpanFull(),

Value::make('closed_at')
->visible(fn (Ticket $record) => ! $record->isOpen())
->label(__('ticket.closed_at'))
->withTime()
->inlineLabel()
->columnSpanFull(),

Value::make('subject')
->label(__('ticket.subject'))
->inlineLabel()
->columnSpanFull(),

Value::make('content')
->label(__('ticket.message'))
->inlineLabel()
->columnSpanFull(),
]);
}

public static function table(Table $table): Table
public static function getRelations(): array
{
return $table
->columns([
//
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
return [
MessagesRelationManager::class,
];
}

public static function getRelations(): array
public static function getWidgets(): array
{
return [
//
OpenTicketsWidget::class,
ClosedTicketsWidget::class,
];
}

Expand All @@ -58,7 +84,7 @@ public static function getPages(): array
return [
'index' => Pages\ListTickets::route('/'),
'create' => Pages\CreateTicket::route('/create'),
'edit' => Pages\EditTicket::route('/{record}/edit'),
'view' => Pages\ViewTicket::route('/{record}'),
];
}
}
Loading

0 comments on commit 9d5b28b

Please sign in to comment.