Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactive demo #23

Closed
dgillier opened this issue Jan 21, 2025 · 2 comments
Closed

Interactive demo #23

dgillier opened this issue Jan 21, 2025 · 2 comments

Comments

@dgillier
Copy link

Hi,

Could you publish your "Interactive Demo" source code?

Thanks,
Denis

@QuentinGab
Copy link
Contributor

The interactive demo only demonstrates the capabilities of the PdfInvoice class.

It's the following Livewire component:

<?php

namespace App\Livewire\LaravelInvoices;

use Brick\Money\Money;
use Carbon\Carbon;
use Finller\Invoice\InvoiceDiscount;
use Finller\Invoice\InvoiceState;
use Finller\Invoice\InvoiceType;
use Finller\Invoice\PdfInvoice;
use Finller\Invoice\PdfInvoiceItem;
use Livewire\Attributes\Url;
use Livewire\Component;
use Symfony\Component\HttpFoundation\StreamedResponse;

class Builder extends Component
{
    #[Url]
    public InvoiceType $type = InvoiceType::Invoice;

    #[Url]
    public InvoiceState $state = InvoiceState::Draft;

    #[Url]
    public ?string $due_at = null;

    #[Url]
    public ?string $paid_at = null;

    #[Url]
    public ?string $description = null;

    #[Url]
    public ?string $serial_number = 'IN24-0001';

    #[Url]
    public ?string $font = 'Helvetica';

    #[Url]
    public string $color = '#050038';

    /**
     * @var array<string, mixed>
     */
    public array $seller = [
        'name' => 'elegantly',
        'address' => [
            'street' => "Place de l'Opéra",
            'city' => 'Paris',
            'postal_code' => '75009',
            'country' => 'France',
        ],
        'email' => '[email protected]',
        'tax_number' => 'FR123456789',
    ];

    /**
     * @var array<string, mixed>
     */
    public array $buyer = [
        'name' => 'John Doe',
        'address' => [
            'street' => '8405 Old James St.Rochester',
            'city' => 'New York',
            'postal_code' => '14609',
            'state' => 'New York (NY)',
            'country' => 'United States',
        ],
        'email' => '[email protected]',
    ];

    /**
     * @var array<int, array<string, mixed>>
     */
    public array $items = [
        [
            'label' => 'Laravel Invoices Package',
            'description' => 'An easy way to manage your invoices in Laravel',
            'quantity' => 1,
            'unit_price' => 100,
        ],
        [
            'label' => 'Laravel Money',
            'description' => 'Use Brick\Money in Laravel',
            'quantity' => 1,
            'unit_price' => 1,
        ],
    ];

    /**
     * @var array<int, array<string, mixed>>
     */
    public array $discounts = [
        [
            'name' => 'Discount',
            'code' => 'SUMMER',
            'percent_off' => 10,
        ],
    ];

    public function mount(): void
    {
        //
    }

    public function download(): StreamedResponse
    {
        $pdf = $this
            ->invoice()
            ->pdf();

        return response()->streamDownload(function () use ($pdf) {
            echo $pdf->output();
        }, 'laravel-invoices-demo.pdf');
    }

    public function invoice(): PdfInvoice
    {
        return new PdfInvoice(
            name: $this->type->trans(),
            state: $this->state->trans(),
            serial_number: $this->serial_number,
            buyer: $this->buyer,
            seller: $this->seller,
            created_at: now(),
            due_at: $this->due_at ? Carbon::parse($this->due_at) : null,
            paid_at: $this->paid_at ? Carbon::parse($this->paid_at) : null,
            description: $this->description,
            color: $this->color,
            font: $this->font,
            discounts: array_map(fn ($item) => new InvoiceDiscount(
                name: $item['name'] ?? '',
                code: $item['code'] ?? null,
                percent_off: $item['percent_off'] ?? 0,
            ), $this->discounts),
            items: array_map(fn ($item) => new PdfInvoiceItem(
                label: data_get($item, 'label'),
                unit_price: data_get($item, 'unit_price') ? Money::of(data_get($item, 'unit_price'), 'USD') : null,
                unit_tax: data_get($item, 'unit_tax') ? Money::of(data_get($item, 'unit_tax'), 'USD') : null,
                tax_percentage: data_get($item, 'tax_percentage'),
                quantity: data_get($item, 'quantity') ?? 1,
                description: data_get($item, 'description'),
                quantity_unit: data_get($item, 'quantity_unit'),
            ), $this->items),
        );
    }

    public function render(): \Illuminate\Contracts\View\View
    {
        return view('livewire.laravel-invoices.builder', [
            'types' => InvoiceType::cases(),
            'states' => InvoiceState::cases(),
            'fonts' => [
                'Helvetica',
                'DejaVu Sans',
                'Courier',
                'Times',
                'Symbol',
                'ZapfDingbats',
            ],
            'invoice' => $this->invoice(),
        ]);
    }
}
<div class="flex gap-4">

    <div class="w-72 flex-none hidden lg:block">
        <form wire:submit.prevent="$refresh" class="flex flex-col gap-3" x-data="{
            discounts: $wire.entangle('discounts', true),
            items: $wire.entangle('items', true),
            addItem() {
                this.items.push({
                    'label': 'Aa',
                    'quantity': 1,
                    'unit_price': 100,
                });
            },
            addDiscount() {
                this.discounts.push({
                    'name': '',
                    'code': '',
                    'percent_off': 10,
                });
            }
        }">

            <x-kit::field label="Font">
                <x-kit::select class="rounded-md" color="white" wire:model.live="font">
                    @foreach ($fonts as $option)
                        <option value="{{ $option }}">{{ $option }}</option>
                    @endforeach
                </x-kit::select>
            </x-kit::field>

            <x-kit::field label="Color">
                <x-kit::input.base type="color" class="rounded-md" color="white" wire:model.live="color" />
            </x-kit::field>

            <x-kit::field label="Serial Number">
                <x-kit::input class="rounded-md" color="white" wire:model.live="serial_number" />
            </x-kit::field>

            <div class="grid grid-cols-2 gap-3">
                <x-kit::field label="Type">
                    <x-kit::select class="rounded-md" color="white" wire:model.live="type">
                        @foreach ($types as $option)
                            <option value="{{ $option->value }}">{{ $option->trans() }}</option>
                        @endforeach
                    </x-kit::select>
                </x-kit::field>

                <x-kit::field label="State">
                    <x-kit::select class="rounded-md" color="white" wire:model.live="state">
                        @foreach ($states as $option)
                            <option value="{{ $option->value }}">{{ $option->trans() }}</option>
                        @endforeach
                    </x-kit::select>
                </x-kit::field>
            </div>

            <div class="grid grid-cols-2 gap-3">
                <x-kit::field label="Due At">
                    <x-kit::input type="date" class="rounded-md" color="white" wire:model.live="due_at" />
                </x-kit::field>

                <x-kit::field label="Paid At">
                    <x-kit::input type="date" class="rounded-md" color="white" wire:model.live="paid_at" />
                </x-kit::field>
            </div>

            <x-kit::field label="Description">
                <x-kit::input.textarea autosized class="rounded-md" color="white" wire:model.live="description" />
            </x-kit::field>

            <x-kit::accordion class="rounded-md bg-white shadow">
                <x-slot:button>
                    <x-kit::accordion.button size="lg" class="w-full justify-between rounded-md font-semibold"
                        icon-right="heroicon-m-chevron-down">
                        Buyer
                    </x-kit::accordion.button>
                </x-slot:button>

                <div class="flex flex-col gap-3 border-t p-3">
                    <x-kit::field label="Name">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="buyer.name" />
                    </x-kit::field>
                    <x-kit::field label="Email">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="buyer.email" />
                    </x-kit::field>
                    <x-kit::field label="Phone">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="buyer.phone_number" />
                    </x-kit::field>
                    <x-kit::field label="VAT number">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="buyer.tax_number" />
                    </x-kit::field>
                </div>

            </x-kit::accordion>

            <x-kit::accordion class="rounded-md bg-white shadow">
                <x-slot:button>
                    <x-kit::accordion.button size="lg" class="w-full justify-between rounded-md font-semibold"
                        icon-right="heroicon-m-chevron-down">
                        Seller
                    </x-kit::accordion.button>
                </x-slot:button>

                <div class="flex flex-col gap-3 border-t p-3">
                    <x-kit::field label="Name">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="seller.name" />
                    </x-kit::field>
                    <x-kit::field label="Email">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="seller.email" />
                    </x-kit::field>
                    <x-kit::field label="Phone">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="seller.phone_number" />
                    </x-kit::field>
                    <x-kit::field label="VAT number">
                        <x-kit::input class="rounded-md" color="white" wire:model.live="seller.tax_number" />
                    </x-kit::field>
                </div>

            </x-kit::accordion>

            <x-kit::accordion class="rounded-md bg-white shadow">
                <x-slot:button>
                    <x-kit::accordion.button size="lg" class="w-full justify-between rounded-md font-semibold"
                        icon-right="heroicon-m-chevron-down">
                        Items
                    </x-kit::accordion.button>
                </x-slot:button>

                <div class="flex flex-col gap-3 divide-y">
                    <template x-for="(item, index) in items" x-bind:key="index">
                        <div class="flex flex-col gap-3 p-3">
                            <x-kit::field label="Label">
                                <x-kit::input class="rounded-md" color="white" x-model="item.label" />
                            </x-kit::field>
                            <x-kit::field label="Description">
                                <x-kit::input.textarea autosized class="rounded-md" color="white"
                                    x-model="item.description" />
                            </x-kit::field>
                            <div class="grid grid-cols-2 gap-2">
                                <x-kit::field label="Quantity">
                                    <x-kit::input type="number" class="rounded-md" color="white"
                                        x-model="item.quantity" />
                                </x-kit::field>
                                <x-kit::field label="Unit price">
                                    <x-kit::input type="number" class="rounded-md" color="white"
                                        x-model="item.unit_price" />
                                </x-kit::field>
                            </div>

                        </div>
                    </template>
                </div>

                <div class="border-t p-3">
                    <x-kit::button x-on:click="addItem" color="white"
                        class="w-full justify-center rounded-md font-semibold">
                        Add
                    </x-kit::button>
                </div>

            </x-kit::accordion>

            <x-kit::accordion class="rounded-md bg-white shadow">
                <x-slot:button>
                    <x-kit::accordion.button size="lg" class="w-full justify-between rounded-md font-semibold"
                        icon-right="heroicon-m-chevron-down">
                        Discounts
                    </x-kit::accordion.button>
                </x-slot:button>

                <div class="flex flex-col gap-3 divide-y">
                    <template x-for="(discount, index) in discounts" x-bind:key="index">
                        <div class="flex flex-col gap-3 p-3">
                            <x-kit::field label="Name">
                                <x-kit::input class="rounded-md" color="white" x-model="discount.name" />
                            </x-kit::field>
                            <div class="grid grid-cols-2 gap-2">
                                <x-kit::field label="Code">
                                    <x-kit::input type="text" class="rounded-md" color="white"
                                        x-model="discount.code" />
                                </x-kit::field>
                                <x-kit::field label="Percent Off">
                                    <x-kit::input type="number" class="rounded-md" color="white"
                                        x-model="discount.percent_off" />
                                </x-kit::field>
                            </div>

                        </div>
                    </template>
                </div>

                <div class="border-t p-3">
                    <x-kit::button x-on:click="addDiscount" color="white"
                        class="w-full justify-center rounded-md font-semibold">
                        Add
                    </x-kit::button>
                </div>

            </x-kit::accordion>

            <x-kit::button wire:click="download" class="justify-between rounded-md font-semibold" color="black"
                icon-right="heroicon-m-arrow-down-tray">
                Download
            </x-kit::button>

        </form>
    </div>

    <div class="grow min-w-0 overflow-auto">
        <div class="sticky top-4 flex aspect-[210/297] flex-col bg-white shadow-md min-w-[40rem]"
            style="
                font-family: '{{ $font }}';
            ">
            @include('invoices::default.invoice', ['invoice' => $invoice])
        </div>
    </div>
</div>

@dgillier
Copy link
Author

Thanks much appreciated !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants