mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-05 18:52:44 +01:00
Merge pull request #4912 from beganovich/v5-1502-client-portal-documents
(v5) Client portal: "Documents" improvements & bug fixes
This commit is contained in:
commit
6872057207
@ -76,10 +76,10 @@ class DocumentController extends Controller
|
||||
|
||||
$options->setSendHttpHeaders(true);
|
||||
|
||||
$zip = new ZipStream('files.zip', $options);
|
||||
$zip = new ZipStream(now() . '-documents.zip', $options);
|
||||
|
||||
foreach ($documents as $document) {
|
||||
$zip->addFileFromPath(basename($document->filePath()), TempFile::path($document->filePath()));
|
||||
$zip->addFileFromPath(basename($document->diskPath()), TempFile::path($document->diskPath()));
|
||||
}
|
||||
|
||||
$zip->finish();
|
||||
|
@ -25,38 +25,15 @@ class DocumentsTable extends Component
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $status = [
|
||||
'resources',
|
||||
];
|
||||
|
||||
public function mount($client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
public function statusChange($status)
|
||||
{
|
||||
if (in_array($status, $this->status)) {
|
||||
return $this->status = array_diff($this->status, [$status]);
|
||||
}
|
||||
|
||||
array_push($this->status, $status);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = $this->client->documents();
|
||||
|
||||
if (in_array('resources', $this->status) && ! in_array('client', $this->status)) {
|
||||
$query = $query->where('documentable_type', '!=', Client::class);
|
||||
}
|
||||
|
||||
if (in_array('client', $this->status) && ! in_array('resources', $this->status)) {
|
||||
$query = $query->where('documentable_type', Client::class);
|
||||
}
|
||||
|
||||
$query = $query
|
||||
->where('is_public', true)
|
||||
$query = $this->client
|
||||
->documents()
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
@ -27,7 +27,8 @@ class ShowDocumentRequest extends FormRequest
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user('contact')->client->id == $this->document->documentable_id;
|
||||
return auth()->user('contact')->client->id == $this->document->documentable_id
|
||||
|| $this->document->documentable->client_id == auth()->user('contact')->client->id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,7 @@ class PortalComposer
|
||||
$data['client'] = auth()->user()->client;
|
||||
$data['settings'] = $this->settings;
|
||||
$data['currencies'] = TranslationHelper::getCurrencies();
|
||||
$data['contact'] = auth('contact')->user();
|
||||
|
||||
$data['multiple_contacts'] = session()->get('multiple_contacts');
|
||||
|
||||
@ -69,8 +70,8 @@ class PortalComposer
|
||||
|
||||
//@todo wire this back in when we are happy with dashboard.
|
||||
// if($this->settings->enable_client_portal_dashboard == TRUE)
|
||||
// $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
|
||||
|
||||
|
||||
// $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
|
||||
$data[] = ['title' => ctrans('texts.invoices'), 'url' => 'client.invoices.index', 'icon' => 'file-text'];
|
||||
$data[] = ['title' => ctrans('texts.recurring_invoices'), 'url' => 'client.recurring_invoices.index', 'icon' => 'file'];
|
||||
$data[] = ['title' => ctrans('texts.payments'), 'url' => 'client.payments.index', 'icon' => 'credit-card'];
|
||||
@ -81,7 +82,7 @@ class PortalComposer
|
||||
|
||||
if (auth()->user('contact')->client->getSetting('enable_client_portal_tasks')) {
|
||||
$data[] = ['title' => ctrans('texts.tasks'), 'url' => 'client.dashboard', 'icon' => 'clock'];
|
||||
|
||||
|
||||
// TODO: Update when 'tasks' module is available in client portal.
|
||||
}
|
||||
|
||||
|
@ -131,4 +131,9 @@ class Document extends BaseModel
|
||||
{
|
||||
return Storage::disk($this->disk)->url($this->url);
|
||||
}
|
||||
|
||||
public function diskPath(): string
|
||||
{
|
||||
return Storage::disk($this->disk)->path($this->url);
|
||||
}
|
||||
}
|
||||
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=1ee684e58f9f6eb754d5",
|
||||
"/css/app.css": "/css/app.css?id=599b11149976e86c83a3",
|
||||
"/css/app.css": "/css/app.css?id=c6df2680d8efda824069",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
|
||||
"/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=206d7de4ac97612980ff",
|
||||
|
@ -4140,7 +4140,8 @@ $LANG = array(
|
||||
'start_migration' => 'Start Migration',
|
||||
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
||||
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
||||
|
||||
'hello' => 'Hello',
|
||||
'group_documents' => 'Group documents',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -15,16 +15,6 @@
|
||||
<svg class="md:hidden" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="8 17 12 21 16 17"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<input wire:click="statusChange('resources')" type="checkbox" class="form-checkbox cursor-pointer" id="resources-checkbox" checked>
|
||||
<label for="resources-checkbox" class="text-sm cursor-pointer">{{ ctrans('texts.resources') }}</label>
|
||||
</div>
|
||||
<div class="mr-3">
|
||||
<input wire:click="statusChange('client')" type="checkbox" class="form-checkbox cursor-pointer" id="client-checkbox">
|
||||
<label for="client-checkbox" class="text-sm cursor-pointer">{{ ctrans('texts.client') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
|
||||
<div class="align-middle inline-block min-w-full overflow-hidden rounded">
|
||||
@ -37,11 +27,6 @@
|
||||
{{ ctrans('texts.name') }}
|
||||
</span>
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
|
||||
<span role="button" wire:click="sortBy('documentable_type')" class="cursor-pointer">
|
||||
{{ ctrans('texts.resource') }}
|
||||
</span>
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
|
||||
<span role="button" wire:click="sortBy('type')" class="cursor-pointer">
|
||||
{{ ctrans('texts.type') }}
|
||||
@ -55,7 +40,7 @@
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
|
||||
{{ ctrans('texts.download') }}
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider" />
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -67,9 +52,6 @@
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ Illuminate\Support\Str::limit($document->name, 20) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ ((new \ReflectionClass($document->documentable))->getShortName()) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ App\Models\Document::$types[$document->type]['mime'] }}
|
||||
</td>
|
||||
@ -106,4 +88,4 @@
|
||||
@endif
|
||||
{{ $documents->links('portal/ninja2020/vendor/pagination') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,5 +12,102 @@
|
||||
@endsection
|
||||
|
||||
@section('body')
|
||||
Coming soon.
|
||||
<div>
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.hello') }}, {{ $contact->first_name }}
|
||||
</h3>
|
||||
|
||||
<div class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
<div class="bg-white overflow-hidden shadow rounded">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<dl>
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 truncate">
|
||||
{{ ctrans('texts.paid_to_date') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-3xl leading-9 font-semibold text-gray-900">
|
||||
{{ App\Utils\Number::formatMoney($client->paid_to_date, $client) }}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white overflow-hidden shadow rounded">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<dl>
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 truncate">
|
||||
{{ ctrans('texts.open_balance') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-3xl leading-9 font-semibold text-gray-900">
|
||||
{{ App\Utils\Number::formatMoney($client->balance, $client) }}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-12 gap-4 mt-6">
|
||||
<div class="col-span-6">
|
||||
<div class="bg-white rounded shadow px-4 py-5 border-b border-gray-200 sm:px-6">
|
||||
<div class="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-no-wrap">
|
||||
<div class="ml-4 mt-4 w-full">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4 capitalize">
|
||||
{{ ctrans('texts.group_documents') }}
|
||||
</h3>
|
||||
|
||||
<div class="flex flex-col h-auto overflow-y-auto">
|
||||
@if($client->group_settings)
|
||||
@forelse($client->group_settings->documents as $document)
|
||||
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
|
||||
class="block inline-flex items-center text-sm button-link text-primary">
|
||||
<span>{{ Illuminate\Support\Str::limit($document->name, 40) }}</span>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" class="ml-2 text-primary h-6 w-4">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||
<polyline points="15 3 21 3 21 9"></polyline>
|
||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||
</svg>
|
||||
</a>
|
||||
@empty
|
||||
<p class="text-sm">{{ ctrans('texts.no_records_found') }}.</p>
|
||||
@endforelse
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-6">
|
||||
<div class="bg-white rounded shadow px-4 py-5 border-b border-gray-200 sm:px-6">
|
||||
<div class="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-no-wrap">
|
||||
<div class="ml-4 mt-4 w-full">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4 capitalize">
|
||||
{{ ctrans('texts.default_documents') }}
|
||||
</h3>
|
||||
|
||||
<div class="flex flex-col h-auto overflow-y-auto">
|
||||
@forelse($client->company->documents as $document)
|
||||
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
|
||||
class="block inline-flex items-center text-sm button-link text-primary">
|
||||
<span>{{ Illuminate\Support\Str::limit($document->name, 40) }}</span>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" class="ml-2 text-primary h-6 w-4">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||
<polyline points="15 3 21 3 21 9"></polyline>
|
||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||
</svg>
|
||||
</a>
|
||||
@empty
|
||||
<p class="text-sm">{{ ctrans('texts.no_records_found') }}.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -13,5 +13,6 @@
|
||||
<form action="{{ route('client.documents.download_multiple') }}" method="post" id="multiple-downloads">
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
@livewire('documents-table', ['client' => $client])
|
||||
@endsection
|
||||
@endsection
|
||||
|
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</form>
|
||||
@else
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4" translate>
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div>
|
||||
@ -57,7 +57,37 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
@if($invoice->documents->count() > 0)
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<p class="text-lg leading-6 font-medium text-gray-900">{{ ctrans('texts.attachments') }}:</p>
|
||||
@foreach($invoice->documents as $document)
|
||||
<div class="inline-flex items-center space-x-1">
|
||||
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
|
||||
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" class="text-primary h-6 w-4">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||
<polyline points="15 3 21 3 21 9"></polyline>
|
||||
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||
</svg>
|
||||
|
||||
@if(!$loop->last)
|
||||
<span>—</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex items-center justify-between mt-4">
|
||||
<section class="flex items-center">
|
||||
<div class="items-center" style="display: none" id="pagination-button-container">
|
||||
<button class="input-label focus:outline-none hover:text-blue-600 transition ease-in-out duration-300" id="previous-page-button" title="Previous page">
|
||||
|
Loading…
Reference in New Issue
Block a user