734 lines
34 KiB
PHP
734 lines
34 KiB
PHP
<?php
|
||
|
||
use App\Models\Company;
|
||
use App\Models\Contact;
|
||
use App\Models\PressRelease;
|
||
use App\Services\Customer\CustomerCompanyContext;
|
||
use App\Services\Image\ImageService;
|
||
use Illuminate\Validation\Rule;
|
||
use Illuminate\Validation\ValidationException;
|
||
use Livewire\Attributes\Layout;
|
||
use Livewire\Attributes\Locked;
|
||
use Livewire\Attributes\Title;
|
||
use Livewire\Volt\Component;
|
||
use Livewire\WithFileUploads;
|
||
|
||
new #[Layout('components.layouts.app'), Title('Firma')] class extends Component {
|
||
use WithFileUploads;
|
||
|
||
#[Locked]
|
||
public int $id;
|
||
|
||
public bool $showCompanyForm = false;
|
||
|
||
public string $companyName = '';
|
||
|
||
public string $companyAddress = '';
|
||
|
||
public string $companyEmail = '';
|
||
|
||
public string $companyPhone = '';
|
||
|
||
public string $companyWebsite = '';
|
||
|
||
public string $companyCountryCode = 'DE';
|
||
|
||
public bool $companyDisableFooterCode = false;
|
||
|
||
public $companyLogo = null;
|
||
|
||
public bool $removeCompanyLogo = false;
|
||
|
||
public bool $showContactForm = false;
|
||
|
||
public ?int $editingContactId = null;
|
||
|
||
public string $contactFirstName = '';
|
||
|
||
public string $contactLastName = '';
|
||
|
||
public string $contactResponsibility = '';
|
||
|
||
public string $contactEmail = '';
|
||
|
||
public string $contactPhone = '';
|
||
|
||
public function mount(int $id): void
|
||
{
|
||
$this->id = $id;
|
||
|
||
$context = app(CustomerCompanyContext::class);
|
||
$company = $context->findFor(auth()->user(), $id);
|
||
|
||
abort_unless($company !== null, 404);
|
||
|
||
$context->select(auth()->user(), $id);
|
||
}
|
||
|
||
public function startEditCompany(): void
|
||
{
|
||
$company = $this->company();
|
||
$this->authorize('update', $company);
|
||
|
||
$this->companyName = (string) $company->name;
|
||
$this->companyAddress = (string) ($company->address ?? '');
|
||
$this->companyEmail = (string) ($company->email ?? '');
|
||
$this->companyPhone = (string) ($company->phone ?? '');
|
||
$this->companyWebsite = (string) ($company->website ?? '');
|
||
$this->companyCountryCode = (string) ($company->country_code ?? 'DE');
|
||
$this->companyDisableFooterCode = (bool) $company->disable_footer_code;
|
||
$this->companyLogo = null;
|
||
$this->removeCompanyLogo = false;
|
||
$this->showCompanyForm = true;
|
||
}
|
||
|
||
public function cancelCompanyForm(): void
|
||
{
|
||
$this->resetCompanyForm();
|
||
}
|
||
|
||
public function saveCompany(ImageService $imageService): void
|
||
{
|
||
$company = $this->company();
|
||
$this->authorize('update', $company);
|
||
|
||
$validated = $this->validate([
|
||
'companyName' => ['required', 'string', 'max:255'],
|
||
'companyAddress' => ['nullable', 'string', 'max:1000'],
|
||
'companyEmail' => ['nullable', 'email', 'max:190'],
|
||
'companyPhone' => ['nullable', 'string', 'max:40'],
|
||
'companyWebsite' => ['nullable', 'url', 'max:190'],
|
||
'companyCountryCode' => ['nullable', 'string', 'size:2', Rule::in(array_keys((array) config('countries.items', [])))],
|
||
'companyLogo' => ['nullable', 'image', 'max:' . (int) (ImageService::MAX_LOGO_BYTES / 1024)],
|
||
]);
|
||
|
||
$company->fill([
|
||
'name' => $validated['companyName'],
|
||
'address' => $validated['companyAddress'] ?: null,
|
||
'email' => $validated['companyEmail'] ?: null,
|
||
'phone' => $validated['companyPhone'] ?: null,
|
||
'website' => $validated['companyWebsite'] ?: null,
|
||
'country_code' => $validated['companyCountryCode'] ?: null,
|
||
'disable_footer_code' => $this->companyDisableFooterCode,
|
||
]);
|
||
|
||
if ($this->removeCompanyLogo) {
|
||
$imageService->deleteCompanyLogo($company->logo_path, $company->logo_variants);
|
||
$company->logo_path = null;
|
||
$company->logo_variants = null;
|
||
}
|
||
|
||
if ($this->companyLogo) {
|
||
$imageService->deleteCompanyLogo($company->logo_path, $company->logo_variants);
|
||
|
||
$stored = $imageService->storeCompanyLogo($this->companyLogo, $company->portal?->value ?? 'presseecho', $company->id);
|
||
|
||
$company->logo_path = $stored['path'];
|
||
$company->logo_variants = $stored['variants'];
|
||
}
|
||
|
||
$company->save();
|
||
$this->resetCompanyForm();
|
||
|
||
session()->flash('company-status', __('Stammdaten wurden gespeichert.'));
|
||
}
|
||
|
||
public function startCreateContact(): void
|
||
{
|
||
$this->authorize('update', $this->company());
|
||
|
||
$this->resetContactForm();
|
||
$this->showContactForm = true;
|
||
}
|
||
|
||
public function editContact(int $contactId): void
|
||
{
|
||
$this->authorize('update', $this->company());
|
||
|
||
$contact = $this->contact($contactId);
|
||
|
||
$this->editingContactId = $contact->id;
|
||
$this->contactFirstName = (string) ($contact->first_name ?? '');
|
||
$this->contactLastName = (string) ($contact->last_name ?? '');
|
||
$this->contactResponsibility = (string) ($contact->responsibility ?? '');
|
||
$this->contactEmail = (string) ($contact->email ?? '');
|
||
$this->contactPhone = (string) ($contact->phone ?? '');
|
||
$this->showContactForm = true;
|
||
}
|
||
|
||
public function cancelContactForm(): void
|
||
{
|
||
$this->resetContactForm();
|
||
}
|
||
|
||
public function saveContact(): void
|
||
{
|
||
$company = $this->company();
|
||
$this->authorize('update', $company);
|
||
|
||
$validated = $this->validate([
|
||
'contactFirstName' => ['nullable', 'string', 'max:80'],
|
||
'contactLastName' => ['nullable', 'string', 'max:80'],
|
||
'contactResponsibility' => ['nullable', 'string', 'max:255'],
|
||
'contactEmail' => ['required', 'email', 'max:255'],
|
||
'contactPhone' => ['nullable', 'string', 'max:40'],
|
||
]);
|
||
|
||
if (blank($validated['contactFirstName']) && blank($validated['contactLastName'])) {
|
||
throw ValidationException::withMessages([
|
||
'contactLastName' => __('Bitte geben Sie mindestens einen Namen an.'),
|
||
]);
|
||
}
|
||
|
||
$payload = [
|
||
'company_id' => $company->id,
|
||
'portal' => $company->portal?->value,
|
||
'first_name' => $validated['contactFirstName'] ?: null,
|
||
'last_name' => $validated['contactLastName'] ?: null,
|
||
'responsibility' => $validated['contactResponsibility'] ?: null,
|
||
'email' => $validated['contactEmail'],
|
||
'phone' => $validated['contactPhone'] ?: null,
|
||
];
|
||
|
||
if ($this->editingContactId) {
|
||
$this->contact($this->editingContactId)->update($payload);
|
||
session()->flash('contact-status', __('Pressekontakt wurde aktualisiert.'));
|
||
} else {
|
||
Contact::query()->create($payload);
|
||
session()->flash('contact-status', __('Pressekontakt wurde angelegt.'));
|
||
}
|
||
|
||
$this->resetContactForm();
|
||
}
|
||
|
||
public function deleteContact(int $contactId): void
|
||
{
|
||
$this->authorize('update', $this->company());
|
||
|
||
$contact = $this->contact($contactId);
|
||
$contact->delete();
|
||
|
||
if ($this->editingContactId === $contactId) {
|
||
$this->resetContactForm();
|
||
}
|
||
|
||
session()->flash('contact-status', __('Pressekontakt wurde gelöscht.'));
|
||
}
|
||
|
||
public function with(): array
|
||
{
|
||
$user = auth()->user();
|
||
$context = app(CustomerCompanyContext::class);
|
||
$company = $context
|
||
->accessibleCompanyQuery($user)
|
||
->withCount(['contacts', 'pressReleases'])
|
||
->findOrFail($this->id);
|
||
|
||
return [
|
||
'company' => $company,
|
||
'roleLabel' => $context->roleLabelFor($company, $user),
|
||
'canManageCompany' => $user->can('update', $company),
|
||
'canManageContacts' => $user->can('update', $company),
|
||
'countries' => (array) config('countries.items', []),
|
||
'contacts' => Contact::withoutGlobalScopes()
|
||
->where('company_id', $company->id)
|
||
->withCount('pressReleases')
|
||
->orderBy('last_name')
|
||
->orderBy('first_name')
|
||
->limit(10)
|
||
->get(['id', 'company_id', 'first_name', 'last_name', 'responsibility', 'email', 'phone']),
|
||
'pressReleases' => PressRelease::withoutGlobalScopes()
|
||
->where('user_id', $user->id)
|
||
->where('company_id', $company->id)
|
||
->latest()
|
||
->limit(10)
|
||
->get(['id', 'title', 'status', 'created_at', 'published_at']),
|
||
];
|
||
}
|
||
|
||
private function company(): Company
|
||
{
|
||
$company = app(CustomerCompanyContext::class)->findFor(auth()->user(), $this->id);
|
||
|
||
abort_unless($company !== null, 404);
|
||
|
||
return $company;
|
||
}
|
||
|
||
private function contact(int $contactId): Contact
|
||
{
|
||
return Contact::withoutGlobalScopes()->where('company_id', $this->id)->findOrFail($contactId);
|
||
}
|
||
|
||
private function resetCompanyForm(): void
|
||
{
|
||
$this->showCompanyForm = false;
|
||
$this->companyName = '';
|
||
$this->companyAddress = '';
|
||
$this->companyEmail = '';
|
||
$this->companyPhone = '';
|
||
$this->companyWebsite = '';
|
||
$this->companyCountryCode = 'DE';
|
||
$this->companyDisableFooterCode = false;
|
||
$this->companyLogo = null;
|
||
$this->removeCompanyLogo = false;
|
||
$this->resetValidation();
|
||
}
|
||
|
||
private function resetContactForm(): void
|
||
{
|
||
$this->showContactForm = false;
|
||
$this->editingContactId = null;
|
||
$this->contactFirstName = '';
|
||
$this->contactLastName = '';
|
||
$this->contactResponsibility = '';
|
||
$this->contactEmail = '';
|
||
$this->contactPhone = '';
|
||
$this->resetValidation();
|
||
}
|
||
}; ?>
|
||
|
||
<div class="space-y-6">
|
||
<flux:card>
|
||
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||
<div class="flex items-start gap-4">
|
||
<div
|
||
class="flex size-16 shrink-0 items-center justify-center rounded-xl border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
|
||
@if ($company->logoUrl())
|
||
<img src="{{ $company->logoUrl() }}" alt="{{ $company->name }}" width="64" height="64"
|
||
class="h-20 max-h-20 w-20 max-w-20 rounded-xl object-contain p-2" />
|
||
@else
|
||
<flux:icon.building-office class="size-8 text-zinc-400" />
|
||
@endif
|
||
</div>
|
||
|
||
<div>
|
||
<div class="flex flex-wrap items-center gap-2">
|
||
<flux:heading size="xl">{{ $company->name }}</flux:heading>
|
||
<flux:badge color="{{ $company->is_active ? 'green' : 'red' }}" size="sm">
|
||
{{ $company->is_active ? __('Aktiv') : __('Inaktiv') }}
|
||
</flux:badge>
|
||
</div>
|
||
<flux:text class="mt-1 text-sm text-zinc-500">{{ $company->slug }}</flux:text>
|
||
<div class="mt-3 flex flex-wrap gap-2">
|
||
<flux:badge color="zinc" size="sm">
|
||
{{ $company->portal?->label() ?? __('Portal unbekannt') }}</flux:badge>
|
||
<flux:badge color="indigo" size="sm">{{ $roleLabel }}</flux:badge>
|
||
<flux:badge color="zinc" size="sm">{{ __('Pressemappe') }}</flux:badge>
|
||
@if ($company->disable_footer_code)
|
||
<flux:badge color="amber" size="sm">{{ __('Footer-Code deaktiviert') }}</flux:badge>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex flex-wrap gap-2">
|
||
<flux:button icon="plus" variant="primary" href="{{ route('me.press-releases.create') }}"
|
||
wire:navigate>
|
||
{{ __('Neue Pressemitteilung') }}
|
||
</flux:button>
|
||
@if ($canManageCompany)
|
||
<flux:button icon="pencil" variant="ghost" wire:click="startEditCompany">
|
||
{{ __('Stammdaten bearbeiten') }}
|
||
</flux:button>
|
||
@endif
|
||
<flux:button icon="arrow-left" variant="ghost" href="{{ route('me.press-kits.index') }}" wire:navigate>
|
||
{{ __('Zurück') }}
|
||
</flux:button>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
|
||
<flux:card>
|
||
<div class="flex flex-wrap gap-2">
|
||
<flux:button size="sm" variant="ghost" href="#stammdaten">{{ __('Stammdaten') }}</flux:button>
|
||
<flux:button size="sm" variant="ghost" href="#pressekontakte">{{ __('Pressekontakte') }}</flux:button>
|
||
<flux:button size="sm" variant="ghost" href="#pressemitteilungen">{{ __('Pressemitteilungen') }}</flux:button>
|
||
<flux:button size="sm" variant="ghost" href="#abrechnung">{{ __('Abrechnung') }}</flux:button>
|
||
<flux:button size="sm" variant="ghost" href="#statistik">{{ __('Statistik') }}</flux:button>
|
||
</div>
|
||
</flux:card>
|
||
|
||
<div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
|
||
<flux:card>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Pressemitteilungen') }}</flux:text>
|
||
<flux:text size="xl" weight="bold">{{ $company->press_releases_count }}</flux:text>
|
||
</flux:card>
|
||
<flux:card>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Pressekontakte') }}</flux:text>
|
||
<flux:text size="xl" weight="bold">{{ $company->contacts_count }}</flux:text>
|
||
</flux:card>
|
||
<flux:card>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Portal') }}</flux:text>
|
||
<flux:text weight="bold">{{ $company->portal?->label() ?? '–' }}</flux:text>
|
||
</flux:card>
|
||
<flux:card>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Deine Rolle') }}</flux:text>
|
||
<flux:text weight="bold">{{ $roleLabel }}</flux:text>
|
||
</flux:card>
|
||
</div>
|
||
|
||
<div class="grid gap-6 xl:grid-cols-2">
|
||
<flux:card id="stammdaten">
|
||
<div class="mb-4 flex items-center justify-between gap-3">
|
||
<div>
|
||
<flux:heading size="lg">{{ __('Stammdaten') }}</flux:heading>
|
||
<flux:text class="text-sm text-zinc-500">{{ __('Firmendaten dieser Firma.') }}</flux:text>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<flux:badge color="{{ $company->is_active ? 'green' : 'red' }}" size="sm">
|
||
{{ $company->is_active ? __('Aktiv') : __('Inaktiv') }}
|
||
</flux:badge>
|
||
@if ($canManageCompany)
|
||
<flux:button size="sm" variant="ghost" icon="pencil" wire:click="startEditCompany">
|
||
{{ __('Bearbeiten') }}
|
||
</flux:button>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
@if (session('company-status'))
|
||
<flux:callout color="green" icon="check-circle" class="mb-4">
|
||
{{ session('company-status') }}
|
||
</flux:callout>
|
||
@endif
|
||
|
||
@if ($showCompanyForm)
|
||
<div class="mb-4 rounded-lg border border-zinc-200 p-4 dark:border-zinc-700">
|
||
<flux:heading size="sm" class="mb-4">
|
||
{{ __('Stammdaten bearbeiten') }}
|
||
</flux:heading>
|
||
|
||
<div class="grid gap-4 sm:grid-cols-2">
|
||
<flux:field>
|
||
<flux:input wire:model="companyName" :label="__('Firmenname')" required />
|
||
<flux:error name="companyName" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:input wire:model="companyEmail" :label="__('E-Mail')" type="email" />
|
||
<flux:error name="companyEmail" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:input wire:model="companyPhone" :label="__('Telefon')" />
|
||
<flux:error name="companyPhone" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:input wire:model="companyWebsite" :label="__('Website')" placeholder="https://..." />
|
||
<flux:error name="companyWebsite" />
|
||
</flux:field>
|
||
<flux:field class="sm:col-span-2">
|
||
<flux:textarea wire:model="companyAddress" :label="__('Adresse')" rows="3" />
|
||
<flux:error name="companyAddress" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:select wire:model="companyCountryCode" :label="__('Land')">
|
||
@foreach ($countries as $code => $name)
|
||
<option value="{{ $code }}">{{ $name }}</option>
|
||
@endforeach
|
||
</flux:select>
|
||
<flux:error name="companyCountryCode" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:checkbox wire:model="companyDisableFooterCode"
|
||
:label="__('Footer-Code deaktivieren')" />
|
||
</flux:field>
|
||
</div>
|
||
|
||
<flux:separator class="my-4" />
|
||
|
||
<div class="space-y-3">
|
||
<flux:heading size="xs">{{ __('Firmenlogo') }}</flux:heading>
|
||
@php($logoUrl = $company->logoUrl())
|
||
@if ($logoUrl && !$removeCompanyLogo)
|
||
<div class="flex items-center gap-3">
|
||
<img src="{{ $logoUrl }}" alt="{{ $company->name }}" width="64"
|
||
height="64"
|
||
class="h-16 max-h-16 w-16 max-w-16 rounded-md border border-zinc-200 object-contain dark:border-zinc-700" />
|
||
<flux:button type="button" size="sm" variant="ghost"
|
||
wire:click="$set('removeCompanyLogo', true)">
|
||
{{ __('Logo entfernen') }}
|
||
</flux:button>
|
||
</div>
|
||
@endif
|
||
<flux:field>
|
||
<flux:input type="file" wire:model="companyLogo" :label="__('Neues Logo hochladen')"
|
||
accept="image/jpeg,image/png,image/webp,image/gif"
|
||
:description="__('JPG/PNG/WebP/GIF, max. 4 MB. Varianten werden automatisch generiert.')" />
|
||
<flux:error name="companyLogo" />
|
||
</flux:field>
|
||
</div>
|
||
|
||
<div class="mt-4 flex justify-end gap-2">
|
||
<flux:button type="button" variant="ghost" wire:click="cancelCompanyForm">
|
||
{{ __('Abbrechen') }}
|
||
</flux:button>
|
||
<flux:button type="button" variant="primary" wire:click="saveCompany">
|
||
{{ __('Speichern') }}
|
||
</flux:button>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<div class="grid gap-3 sm:grid-cols-2">
|
||
<div>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('E-Mail') }}</flux:text>
|
||
<flux:text>{{ $company->email ?: '–' }}</flux:text>
|
||
</div>
|
||
<div>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Telefon') }}</flux:text>
|
||
<flux:text>{{ $company->phone ?: '–' }}</flux:text>
|
||
</div>
|
||
<div>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Website') }}</flux:text>
|
||
@if ($company->website)
|
||
<a href="{{ $company->website }}" target="_blank"
|
||
class="text-sm text-blue-600 hover:underline dark:text-blue-400">{{ $company->website }}</a>
|
||
@else
|
||
<flux:text>–</flux:text>
|
||
@endif
|
||
</div>
|
||
<div>
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Land') }}</flux:text>
|
||
<flux:text>{{ $company->country_code ?: '–' }}</flux:text>
|
||
</div>
|
||
<div class="sm:col-span-2">
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Adresse') }}</flux:text>
|
||
<flux:text>{{ $company->address ?: '–' }}</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
|
||
<flux:card id="pressekontakte">
|
||
<div class="mb-4 flex items-center justify-between">
|
||
<div>
|
||
<flux:heading size="lg">{{ __('Pressekontakte') }}</flux:heading>
|
||
<flux:text class="text-sm text-zinc-500">
|
||
{{ trans_choice(':count Kontakt|:count Kontakte', $company->contacts_count, ['count' => $company->contacts_count]) }}
|
||
</flux:text>
|
||
</div>
|
||
|
||
@if ($canManageContacts)
|
||
<flux:button size="sm" variant="primary" icon="plus" wire:click="startCreateContact">
|
||
{{ __('Kontakt hinzufügen') }}
|
||
</flux:button>
|
||
@endif
|
||
</div>
|
||
|
||
@if (session('contact-status'))
|
||
<flux:callout color="green" icon="check-circle" class="mb-4">
|
||
{{ session('contact-status') }}
|
||
</flux:callout>
|
||
@endif
|
||
|
||
@if ($showContactForm)
|
||
<div class="mb-4 rounded-lg border border-zinc-200 p-4 dark:border-zinc-700">
|
||
<flux:heading size="sm" class="mb-4">
|
||
{{ $editingContactId ? __('Pressekontakt bearbeiten') : __('Neuen Pressekontakt anlegen') }}
|
||
</flux:heading>
|
||
|
||
<div class="grid gap-4 sm:grid-cols-2">
|
||
<flux:field>
|
||
<flux:input wire:model="contactFirstName" :label="__('Vorname')" />
|
||
<flux:error name="contactFirstName" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:input wire:model="contactLastName" :label="__('Nachname')" />
|
||
<flux:error name="contactLastName" />
|
||
</flux:field>
|
||
<flux:field class="sm:col-span-2">
|
||
<flux:input wire:model="contactResponsibility" :label="__('Position / Rolle')" />
|
||
<flux:error name="contactResponsibility" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:input wire:model="contactEmail" :label="__('E-Mail')" type="email" required />
|
||
<flux:error name="contactEmail" />
|
||
</flux:field>
|
||
<flux:field>
|
||
<flux:input wire:model="contactPhone" :label="__('Telefon')" />
|
||
<flux:error name="contactPhone" />
|
||
</flux:field>
|
||
</div>
|
||
|
||
<div class="mt-4 flex justify-end gap-2">
|
||
<flux:button type="button" variant="ghost" wire:click="cancelContactForm">
|
||
{{ __('Abbrechen') }}
|
||
</flux:button>
|
||
<flux:button type="button" variant="primary" wire:click="saveContact">
|
||
{{ __('Speichern') }}
|
||
</flux:button>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<div class="space-y-3">
|
||
@forelse($contacts as $contact)
|
||
<div class="rounded-lg bg-zinc-50 p-3 dark:bg-zinc-900">
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||
<div>
|
||
<flux:text weight="semibold">
|
||
{{ trim(($contact->first_name ?? '') . ' ' . ($contact->last_name ?? '')) ?: __('Kontakt ohne Name') }}
|
||
</flux:text>
|
||
<flux:text class="text-sm text-zinc-500">
|
||
{{ $contact->responsibility ?: __('Keine Rolle hinterlegt') }}</flux:text>
|
||
<div class="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-xs text-zinc-500">
|
||
@if ($contact->email)
|
||
<a href="mailto:{{ $contact->email }}"
|
||
class="text-blue-600 hover:underline dark:text-blue-400">{{ $contact->email }}</a>
|
||
@endif
|
||
@if ($contact->phone)
|
||
<span>{{ $contact->phone }}</span>
|
||
@endif
|
||
@if ($contact->press_releases_count > 0)
|
||
<span>{{ trans_choice('in :count Pressemitteilung hinterlegt|in :count Pressemitteilungen hinterlegt', $contact->press_releases_count, ['count' => $contact->press_releases_count]) }}</span>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
@if ($canManageContacts)
|
||
<div class="flex gap-1">
|
||
<flux:button size="sm" variant="ghost" icon="pencil"
|
||
wire:click="editContact({{ $contact->id }})">
|
||
{{ __('Bearbeiten') }}
|
||
</flux:button>
|
||
<flux:button size="sm" variant="ghost" icon="trash"
|
||
wire:click="deleteContact({{ $contact->id }})"
|
||
wire:confirm="{{ __('Diesen Pressekontakt löschen?') }}">
|
||
{{ __('Löschen') }}
|
||
</flux:button>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
@empty
|
||
<div class="rounded-lg border border-dashed border-zinc-200 p-4 text-sm text-zinc-500 dark:border-zinc-700">
|
||
<flux:text weight="semibold">{{ __('Keine Pressekontakte hinterlegt') }}</flux:text>
|
||
<flux:text class="mt-1 text-sm text-zinc-500">
|
||
{{ __('Pressekontakte helfen, Pressemitteilungen eindeutig einer Ansprechperson zuzuordnen.') }}
|
||
</flux:text>
|
||
@if ($canManageContacts)
|
||
<flux:button class="mt-4" size="sm" variant="primary" icon="plus" wire:click="startCreateContact">
|
||
{{ __('Kontakt hinzufügen') }}
|
||
</flux:button>
|
||
@endif
|
||
</div>
|
||
@endforelse
|
||
</div>
|
||
</flux:card>
|
||
</div>
|
||
|
||
<flux:card id="pressemitteilungen" class="p-0">
|
||
<div class="flex items-center justify-between border-b border-zinc-200 px-4 py-3 dark:border-zinc-700">
|
||
<flux:heading size="lg">{{ __('Pressemitteilungen dieser Firma') }}</flux:heading>
|
||
<flux:button size="sm" variant="ghost" href="{{ route('me.press-releases.index') }}" wire:navigate>
|
||
{{ __('Alle anzeigen') }}
|
||
</flux:button>
|
||
</div>
|
||
|
||
<div class="px-4 pb-4 pt-2">
|
||
<flux:table>
|
||
<flux:table.columns>
|
||
<flux:table.column>{{ __('Titel') }}</flux:table.column>
|
||
<flux:table.column>{{ __('Status') }}</flux:table.column>
|
||
<flux:table.column>{{ __('Datum') }}</flux:table.column>
|
||
<flux:table.column>{{ __('Aktionen') }}</flux:table.column>
|
||
</flux:table.columns>
|
||
|
||
@forelse($pressReleases as $pressRelease)
|
||
<flux:table.row wire:key="company-pr-{{ $pressRelease->id }}">
|
||
<flux:table.cell>
|
||
<flux:text weight="semibold">{{ $pressRelease->title }}</flux:text>
|
||
</flux:table.cell>
|
||
<flux:table.cell>
|
||
<flux:badge
|
||
color="{{ match ($pressRelease->status->value) {
|
||
'published' => 'green',
|
||
'review' => 'yellow',
|
||
'rejected' => 'red',
|
||
'archived' => 'blue',
|
||
default => 'zinc',
|
||
} }}"
|
||
size="sm">
|
||
{{ $pressRelease->status->label() }}
|
||
</flux:badge>
|
||
</flux:table.cell>
|
||
<flux:table.cell>
|
||
<flux:text class="text-sm text-zinc-500">
|
||
{{ $pressRelease->published_at?->format('d.m.Y') ?? ($pressRelease->created_at?->format('d.m.Y') ?? '–') }}
|
||
</flux:text>
|
||
</flux:table.cell>
|
||
<flux:table.cell>
|
||
<flux:button size="sm" variant="ghost" icon="eye"
|
||
href="{{ route('me.press-releases.show', $pressRelease->id) }}" wire:navigate>
|
||
{{ __('Öffnen') }}
|
||
</flux:button>
|
||
</flux:table.cell>
|
||
</flux:table.row>
|
||
@empty
|
||
<flux:table.row>
|
||
<flux:table.cell colspan="4">
|
||
<div class="flex flex-col items-center justify-center px-4 py-10 text-center">
|
||
<flux:icon.newspaper class="size-10 text-zinc-300" />
|
||
<flux:text weight="semibold" class="mt-3">
|
||
{{ __('Keine Pressemitteilungen für diese Firma') }}
|
||
</flux:text>
|
||
<flux:text class="mt-1 max-w-md text-sm text-zinc-500">
|
||
{{ __('Erstellen Sie die erste Pressemitteilung direkt mit dieser Firma als Kontext.') }}
|
||
</flux:text>
|
||
<flux:button class="mt-4" size="sm" variant="primary" icon="plus" href="{{ route('me.press-releases.create') }}" wire:navigate>
|
||
{{ __('Neue Pressemitteilung') }}
|
||
</flux:button>
|
||
</div>
|
||
</flux:table.cell>
|
||
</flux:table.row>
|
||
@endforelse
|
||
</flux:table>
|
||
</div>
|
||
</flux:card>
|
||
|
||
<div class="grid gap-6 xl:grid-cols-2">
|
||
<flux:card id="abrechnung">
|
||
<div class="flex items-start justify-between gap-3">
|
||
<div>
|
||
<flux:heading size="lg">{{ __('Abrechnung') }}</flux:heading>
|
||
<flux:text class="mt-1 text-sm text-zinc-500">
|
||
{{ __('Firmenspezifische Zahlungsarten und Add-ons werden hier später zusammengeführt.') }}
|
||
</flux:text>
|
||
</div>
|
||
<flux:badge color="zinc" size="sm">{{ __('In Vorbereitung') }}</flux:badge>
|
||
</div>
|
||
|
||
<div class="mt-4 rounded-lg border border-dashed border-zinc-200 p-4 dark:border-zinc-700">
|
||
<flux:text weight="semibold">{{ __('Noch keine firmenspezifische Abrechnung') }}</flux:text>
|
||
<flux:text class="mt-1 text-sm text-zinc-500">
|
||
{{ __('Rechnungen finden Sie aktuell gesammelt im Finanzbereich. Firmenscharfe Zahlungsarten folgen mit dem Preismodell.') }}
|
||
</flux:text>
|
||
<flux:button class="mt-4" size="sm" variant="ghost" href="{{ route('me.invoices.index') }}" wire:navigate>
|
||
{{ __('Rechnungen öffnen') }}
|
||
</flux:button>
|
||
</div>
|
||
</flux:card>
|
||
|
||
<flux:card id="statistik">
|
||
<div class="flex items-start justify-between gap-3">
|
||
<div>
|
||
<flux:heading size="lg">{{ __('Statistik') }}</flux:heading>
|
||
<flux:text class="mt-1 text-sm text-zinc-500">
|
||
{{ __('Erste Kennzahlen zur Firma; detaillierte Auswertungen folgen später.') }}
|
||
</flux:text>
|
||
</div>
|
||
<flux:badge color="zinc" size="sm">{{ __('Später') }}</flux:badge>
|
||
</div>
|
||
|
||
<div class="mt-4 grid grid-cols-2 gap-3">
|
||
<div class="rounded-lg bg-zinc-50 p-3 dark:bg-zinc-900">
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Pressemitteilungen') }}</flux:text>
|
||
<flux:text size="lg" weight="bold">{{ $company->press_releases_count }}</flux:text>
|
||
</div>
|
||
<div class="rounded-lg bg-zinc-50 p-3 dark:bg-zinc-900">
|
||
<flux:text class="text-xs text-zinc-500">{{ __('Pressekontakte') }}</flux:text>
|
||
<flux:text size="lg" weight="bold">{{ $company->contacts_count }}</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</div>
|
||
</div>
|