Add all files
This commit is contained in:
21
themes/default/views/services/cancel.blade.php
Normal file
21
themes/default/views/services/cancel.blade.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
{{ __('services.cancel_are_you_sure') }}
|
||||
|
||||
<x-form.select name="type" label="{{ __('services.cancel_type') }}" required wire:model="type">
|
||||
<option value="end_of_period">{{ __('services.cancel_end_of_period') }}</option>
|
||||
<option value="immediate">{{ __('services.cancel_immediate') }}</option>
|
||||
</x-form.select>
|
||||
|
||||
<x-form.textarea name="reason" label="{{ __('services.cancel_reason') }}" required wire:model="reason" />
|
||||
|
||||
<!-- Show you'll lose data warning if immediate cancellation is selected -->
|
||||
<template x-if="$wire.type === 'immediate'">
|
||||
<div class="bg-orange-700 text-white p-4 rounded">
|
||||
{{ __('services.cancel_immediate_warning') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<x-button.danger wire:confirm="Are you sure?" wire:click="cancelService">
|
||||
{{ __('services.cancel') }}
|
||||
</x-button.danger>
|
||||
</div>
|
||||
44
themes/default/views/services/index.blade.php
Normal file
44
themes/default/views/services/index.blade.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<div class="space-y-4">
|
||||
<x-navigation.breadcrumb />
|
||||
@foreach ($services as $service)
|
||||
<a href="{{ route('services.show', $service) }}" wire:navigate>
|
||||
<div class="bg-background-secondary hover:bg-background-secondary/80 border border-neutral p-4 rounded-lg mb-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-secondary/10 p-2 rounded-lg">
|
||||
<x-ri-instance-line class="size-5 text-secondary" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium">{{ $service->product->name }}</span>
|
||||
@if(optional($service->product->server)->extension === 'Proxmox' && $service->proxmoxServer && $service->proxmoxServer->hostname)
|
||||
<span class="text-sm text-base/60">({{ $service->proxmoxServer->hostname }})</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="size-5 rounded-md p-0.5
|
||||
@if ($service->status == 'active') text-success bg-success/20
|
||||
@elseif($service->status == 'suspended' || $service->status == 'cancelled') text-inactive bg-inactive/20
|
||||
@else text-warning bg-warning/20
|
||||
@endif">
|
||||
@if ($service->status == 'active')
|
||||
<x-ri-checkbox-circle-fill />
|
||||
@elseif($service->status == 'suspended' || $service->status == 'cancelled')
|
||||
<x-ri-forbid-fill />
|
||||
@elseif($service->status == 'pending')
|
||||
<x-ri-error-warning-fill />
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-base text-sm">Product(s): {{ $service->product->category->name }} {{
|
||||
in_array($service->plan->type, ['recurring']) ? ' - ' . __('services.every_period', [
|
||||
'period' => $service->plan->billing_period > 1 ? $service->plan->billing_period : '',
|
||||
'unit' => trans_choice(__('services.billing_cycles.' . $service->plan->billing_unit),
|
||||
$service->plan->billing_period)
|
||||
]) : '' }} {{ $service->expires_at ? '- ' . __('services.expires_at') . ': '. $service->expires_at->format('M d, Y') : ''}}</p>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
|
||||
{{ $services->links() }}
|
||||
</div>
|
||||
143
themes/default/views/services/show.blade.php
Normal file
143
themes/default/views/services/show.blade.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<div>
|
||||
@if($invoice = $service->invoices()->where('status', 'pending')->first())
|
||||
<div class="w-full mb-4">
|
||||
<div class="bg-yellow-600/20 border-l-4 border-yellow-500 text-yellow-300 p-4 rounded-lg">
|
||||
<p class="font-medium">
|
||||
⚠️ {{ __('services.outstanding_invoice') }}
|
||||
<a href="{{ route('invoices.show', $invoice)}}"
|
||||
class="underline hover:text-yellow-100 underline-offset-2">{{ __('services.view_and_pay') }}</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="bg-background-secondary border border-neutral p-6 rounded-lg mt-2">
|
||||
<div class="flex flex-col md:flex-row justify-between">
|
||||
<h1 class="text-2xl font-semibold">{{ __('services.services') }}</h1>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-2 gap-4 my-4">
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold">{{ __('services.product_details') }}:</h4>
|
||||
<div class="mt-2">
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.name') }}:</span>
|
||||
<span class="text-base/50">{{ $service->product->name }}</span>
|
||||
</div>
|
||||
@if(isset($proxmoxServer) && $proxmoxServer)
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">Hostname:</span>
|
||||
<span class="text-base/50">{{ $proxmoxServer->hostname }}</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.price') }}:</span>
|
||||
<span class="text-base/50">{{ $service->formattedPrice }}</span>
|
||||
</div>
|
||||
@if($service->plan->type == 'recurring')
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.billing_cycle') }}:</span>
|
||||
<span class="text-base/50">{{ __('services.every_period', [
|
||||
'period' => $service->plan->billing_period > 1 ? $service->plan->billing_period : '',
|
||||
'unit' => trans_choice(__('services.billing_cycles.' . $service->plan->billing_unit),
|
||||
$service->plan->billing_period)
|
||||
])
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.expires_at') }}:</span>
|
||||
<span class="text-base/50">{{ $service->expires_at ? $service->expires_at->format('M d, Y')
|
||||
: 'N/A' }}</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.status') }}:</span>
|
||||
<span
|
||||
class="font-semibold @if ($service->status == 'active') text-green-500 @elseif($service->status == 'cancelled') text-red-500 @else text-orange-500 @endif">
|
||||
{{ __('services.statuses.' . $service->status) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if($service->cancellable || $service->upgradable || count($buttons) > 0)
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold">{{ __('services.actions') }}:</h4>
|
||||
<div class="mt-2 flex flex-row gap-2 flex-wrap">
|
||||
@if($service->upgradable)
|
||||
<a href="{{ route('services.upgrade', $service->id) }}">
|
||||
<x-button.primary class="h-fit !w-fit">
|
||||
<span>{{ __('services.upgrade') }}</span>
|
||||
</x-button.primary>
|
||||
</a>
|
||||
@endif
|
||||
@if($service->upgrade()->where('status', 'pending')->exists())
|
||||
<x-button.primary class="h-fit !w-fit"
|
||||
@click="Alpine.store('notifications').addNotification([{message: '{{ __('services.upgrade_pending') }}', type: 'error'}])">
|
||||
<span>{{ __('services.upgrade') }}</span>
|
||||
</x-button.primary>
|
||||
@endif
|
||||
@if($service->cancellable)
|
||||
<x-button.danger class="h-fit !w-fit" wire:click="$set('showCancel', true)">
|
||||
<span wire:loading.remove wire:target="$set('showCancel', true)">{{ __('services.cancel')
|
||||
}}</span>
|
||||
<x-loading target="$set('showCancel', true)" />
|
||||
</x-button.danger>
|
||||
@endif
|
||||
@if($showCancel)
|
||||
<x-modal open="true"
|
||||
title="{{ __('services.cancellation', ['service' => $service->product->name]) }}"
|
||||
width="max-w-3xl">
|
||||
<livewire:services.cancel :service="$service" />
|
||||
<x-slot name="closeTrigger">
|
||||
<div class="flex gap-4">
|
||||
<button wire:click="$set('showCancel', false)" @click="open = false"
|
||||
class="text-primary-100">
|
||||
<x-ri-close-fill class="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-modal>
|
||||
@endif
|
||||
</div>
|
||||
<div class="mt-2 flex flex-row gap-2 flex-wrap">
|
||||
@foreach ($buttons as $button)
|
||||
<!-- If the button has a function then call it when clicked -->
|
||||
@if (isset($button['function']))
|
||||
<x-button.primary class="h-fit !w-fit" wire:click="goto('{{ $button['function'] }}')">
|
||||
{{ $button['label'] }}
|
||||
</x-button.primary>
|
||||
@else
|
||||
<a href="{{ $button['url'] }}"
|
||||
@if(!empty($button['target'])) target="{{ $button['target'] }}" @endif
|
||||
@if(($button['target'] ?? null) === '_blank') rel="noopener noreferrer" @endif>
|
||||
<x-button.primary class="h-fit !w-fit">
|
||||
{{ $button['label'] }}
|
||||
</x-button.primary>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (count($views) > 0)
|
||||
<div class="bg-primary-800 rounded-lg mt-2">
|
||||
@if (count($views) > 1)
|
||||
<div class="flex w-fit mb-2 flex-row flex-wrap">
|
||||
@foreach ($views as $view)
|
||||
<button wire:click="changeView('{{ $view['name'] }}')"
|
||||
class="px-4 py-2 -mb-px focus:outline-none {{ $view['name'] == $currentView ? 'border-b-2 border-gray-400 font-semibold' : 'text-base border-b border-gray-500 ' }}">
|
||||
{{ $view['label'] }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- show loading spinner -->
|
||||
<x-loading target="changeView" />
|
||||
<div wire:loading.remove wire:target="changeView">
|
||||
{!! $extensionView !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
154
themes/default/views/services/upgrade.blade.php
Normal file
154
themes/default/views/services/upgrade.blade.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">
|
||||
{{ __('services.upgrade_service', ['service' => $service->product->name]) }}
|
||||
</h1>
|
||||
|
||||
<h2 class="text-lg font-semibold mt-4">
|
||||
@if($step == 1)
|
||||
{{ __('services.upgrade_choose_product') }}
|
||||
@else
|
||||
{{ __('services.upgrade_choose_config') }}
|
||||
@endif
|
||||
</h2>
|
||||
|
||||
|
||||
<div class="grid grid-cols-3 gap-6 mt-2">
|
||||
<div class="grid md:grid-cols-2 gap-6 col-span-2">
|
||||
@if($step == 1)
|
||||
{{-- Show current product, we also allow config upgrades so they can use that --}}
|
||||
<div>
|
||||
<input type="radio" name="upgrade" value="{{ $service->product->id }}" wire:model.live="upgrade"
|
||||
class="hidden peer" id="product-{{ $service->product->id }}">
|
||||
<label for="product-{{ $service->product->id }}"
|
||||
class="flex flex-col cursor-pointer bg-background-secondary hover:bg-background-secondary/80 border border-neutral peer-checked:border-secondary p-4 rounded-lg">
|
||||
<div
|
||||
class="rounded-full border border-background rounded-selector inline-flex items-center justify-center gap-2 align-middle bg-primary/60 w-fit px-2 py-0.5">
|
||||
<p class="">{{ __('services.current_plan') }}</p>
|
||||
</div>
|
||||
@if(theme('small_images', false))
|
||||
<div class="flex gap-x-3 items-center">
|
||||
@endif
|
||||
@if ($service->product->image)
|
||||
<img src="{{ Storage::url($service->product->image) }}" alt="{{ $service->product->name }}"
|
||||
class="rounded-md {{ theme('small_images', false) ? 'w-14 h-fit' : 'w-full object-cover object-center' }}">
|
||||
@endif
|
||||
<h2 class="text-xl font-bold">{{ $service->product->name }}</h2>
|
||||
@if(theme('small_images', false))
|
||||
</div>
|
||||
@endif
|
||||
<article class="prose dark:prose-invert">
|
||||
{!! $service->product->description !!}
|
||||
</article>
|
||||
<h3 class="text-lg font-semibold mb-2">
|
||||
{{ __('services.price_every_period', [
|
||||
'price' => $service->product->price(null, $service->plan->billing_period, $service->plan->billing_unit,
|
||||
$service->currency_code),
|
||||
'period' => $service->plan->billing_period > 1 ? $service->plan->billing_period : '',
|
||||
'unit' => strtolower(trans_choice(__('services.billing_cycles.' . $service->plan->billing_unit), $service->plan->billing_period))
|
||||
]) }}
|
||||
</h3>
|
||||
</label>
|
||||
</div>
|
||||
@foreach ($service->productUpgrades() as $product)
|
||||
<div>
|
||||
<input type="radio" name="upgrade" value="{{ $product->id }}" wire:model.live="upgrade"
|
||||
class="hidden peer" id="product-{{ $product->id }}">
|
||||
<label for="product-{{ $product->id }}"
|
||||
class="flex flex-col cursor-pointer bg-background-secondary hover:bg-background-secondary/80 border border-neutral peer-checked:border-secondary p-4 rounded-lg">
|
||||
@if($upgrade == $product->id)
|
||||
<div
|
||||
class="rounded-full border border-background rounded-selector inline-flex items-center justify-center gap-2 align-middle bg-primary w-fit px-2 py-0.5">
|
||||
<p class="">{{ __('services.new_plan') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
@if(theme('small_images', false))
|
||||
<div class="flex gap-x-3 items-center">
|
||||
@endif
|
||||
@if ($product->image)
|
||||
<img src="{{ Storage::url($product->image) }}" alt="{{ $product->name }}"
|
||||
class="rounded-md {{ theme('small_images', false) ? 'w-14 h-fit' : 'w-full object-cover object-center' }}">
|
||||
@endif
|
||||
<h2 class="text-xl font-bold">{{ $product->name }}</h2>
|
||||
@if(theme('small_images', false))
|
||||
</div>
|
||||
@endif
|
||||
<article class="prose dark:prose-invert">
|
||||
{!! $product->description !!}
|
||||
</article>
|
||||
<h3 class="text-lg font-semibold mb-2">
|
||||
{{ __('services.price_every_period', [
|
||||
'price' => $product->price(null, $service->plan->billing_period, $service->plan->billing_unit,
|
||||
$service->currency_code),
|
||||
'period' => $service->plan->billing_period > 1 ? $service->plan->billing_period : '',
|
||||
'unit' => trans_choice(__('services.billing_cycles.' . $service->plan->billing_unit), $service->plan->billing_period)
|
||||
]) }}
|
||||
</h3>
|
||||
</label>
|
||||
</div>
|
||||
@endforeach
|
||||
@else
|
||||
<div class="col-span-2 flex flex-col gap-4">
|
||||
@foreach ($upgradeProduct->upgradableConfigOptions as $configOption)
|
||||
@php
|
||||
$showPriceTag = $configOption->children->filter(fn ($value) => !$value->price(billing_period: $service->plan->billing_period, billing_unit: $service->plan->billing_unit)->is_free)->count() > 0;
|
||||
@endphp
|
||||
<x-form.configoption :config="$configOption" :name="'configOptions.' . $configOption->id" :showPriceTag="$showPriceTag" :plan="$service->plan">
|
||||
{{-- If the config option is a select, show the options --}}
|
||||
@if ($configOption->type == 'select')
|
||||
@foreach ($configOption->children as $configOptionValue)
|
||||
<option value="{{ $configOptionValue->id }}">
|
||||
{{ $configOptionValue->name }}
|
||||
{{ ($showPriceTag && $configOptionValue->price(billing_period: $service->billing_period, billing_unit: $service->billing_unit)->available) ? ' - ' . $configOptionValue->price(billing_period: $service->billing_period, billing_unit: $service->billing_unit) : '' }}
|
||||
</option>
|
||||
@endforeach
|
||||
@elseif($configOption->type == 'radio')
|
||||
@foreach ($configOption->children as $configOptionValue)
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="radio" id="{{ $configOptionValue->id }}" name="{{ $configOption->id }}"
|
||||
wire:model.live="configOptions.{{ $configOption->id }}"
|
||||
value="{{ $configOptionValue->id }}" />
|
||||
<label for="{{ $configOptionValue->id }}">
|
||||
{{ $configOptionValue->name }}
|
||||
{{ ($showPriceTag && $configOptionValue->price(billing_period: $service->billing_period, billing_unit: $service->billing_unit)->available) ? ' - ' . $configOptionValue->price(billing_period: $service->billing_period, billing_unit: $service->billing_unit) : '' }}
|
||||
</label>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</x-form.configoption>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 w-full col-span-1 bg-background-secondary p-3 rounded-md h-fit">
|
||||
<h4 class="text-lg font-semibold">{{ __('services.upgrade_summary') }}:</h4>
|
||||
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.current_plan') }}:</span>
|
||||
<span class="text-base/50">{{ $service->product->name }}</span>
|
||||
</div>
|
||||
@if($upgrade != $service->product->id)
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.new_plan') }}:</span>
|
||||
<span class="text-base/50">{{ $upgradeProduct ? $upgradeProduct->name : __('general.select_plan') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Total today --}}
|
||||
<div class="flex items-center text-base">
|
||||
<span class="mr-2">{{ __('services.total_today') }}:</span>
|
||||
<span class="text-base/50">{{ $this->totalToday() }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row justify-end gap-2 mt-2">
|
||||
<x-button.primary class="h-fit" :wire:click="($upgradeProduct->upgradableConfigOptions()->count() > 0 && $step == 1)? 'nextStep' : 'doUpgrade'">
|
||||
{{-- If the next upgradeProduct supports config upgrades, show those --}}
|
||||
@if($upgradeProduct && $upgradeProduct->upgradableConfigOptions()->count() > 0 && $step == 1)
|
||||
<span>{{ __('services.next_step') }}</span>
|
||||
@else
|
||||
<span>{{ __('services.upgrade') }}</span>
|
||||
@endif
|
||||
</x-button.primary>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
33
themes/default/views/services/widget.blade.php
Normal file
33
themes/default/views/services/widget.blade.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<div class="space-y-4">
|
||||
@foreach ($services as $service)
|
||||
<a href="{{ route('services.show', $service) }}" wire:navigate>
|
||||
<div class="bg-background-secondary hover:bg-background-secondary/80 border border-neutral p-4 rounded-lg mb-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-secondary/10 p-2 rounded-lg">
|
||||
<x-ri-instance-line class="size-5 text-secondary" />
|
||||
</div>
|
||||
<span class="font-medium">{{ $service->product->name }}</span>
|
||||
</div>
|
||||
<div class="size-5 rounded-md p-0.5
|
||||
@if ($service->status == 'active') text-success bg-success/20
|
||||
@elseif($service->status == 'suspended') text-inactive bg-inactive/20
|
||||
@else text-warning bg-warning/20
|
||||
@endif">
|
||||
@if ($service->status == 'active')
|
||||
<x-ri-checkbox-circle-fill />
|
||||
@elseif($service->status == 'suspended')
|
||||
<x-ri-forbid-fill />
|
||||
@elseif($service->status == 'pending')
|
||||
<x-ri-error-warning-fill />
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-base text-sm">Product(s): {{ $service->product->category->name }} {{ in_array($service->plan->type, ['recurring']) ? ' - ' . __('services.every_period', [
|
||||
'period' => $service->plan->billing_period > 1 ? $service->plan->billing_period : '',
|
||||
'unit' => trans_choice(__('services.billing_cycles.' . $service->plan->billing_unit), $service->plan->billing_period)
|
||||
]) : '' }} {{ $service->expires_at ? '- ' . __('services.expires_at') . ': '. $service->expires_at->format('M d, Y') : ''}}</p>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
Reference in New Issue
Block a user