Added Filament

orders
Nisse Lommerde 2 months ago committed by Nisse Lommerde
parent 08f0a99551
commit 542e1346f4

@ -0,0 +1,8 @@
[Dolphin]
Timestamp=2024,10,17,12,9,4.965
Version=4
ViewMode=1
VisibleRoles=Icons_text,Icons_size
[Settings]
HiddenFilesShown=true

@ -0,0 +1,41 @@
<?php
namespace App\Enums;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
enum OrderAttributes: string implements HasIcon, HasLabel
{
case new_art = 'New Art';
case repeat = 'Repeat';
case rush = 'Rush';
case event = 'Event';
case digitizing = 'Digitizing';
case garments = 'Garments';
case supplied_file = 'Customer Supplied File';
public function getLabel(): ?string
{
return $this->value;
}
// public function getColor(): string|array|null
// {
// return match ($this) {
// };
// }
public function getIcon(): ?string
{
return match ($this) {
self::new_art => 'heroicon-o-paint-brush',
self::repeat => 'heroicon-o-clipboard-document',
self::rush => 'heroicon-o-clock',
self::event => 'heroicon-o-users',
self::digitizing => 'heroicon-o-computer-desktop',
self::garments => 'heroicon-o-shopping-bag',
self::supplied_file => 'heroicon-o-arrow-down-tray',
};
}
}

@ -2,10 +2,42 @@
namespace App\Enums;
enum OrderStatus: string
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
enum OrderStatus: string implements HasColor, HasIcon, HasLabel
{
case DRAFT = 'Draft';
case APPROVED = 'Approved';
case PRODUCTION = 'Production';
case SHIPPED = 'Shipped';
case INVOICED = 'Invoiced';
public function getLabel(): ?string
{
return $this->value;
}
public function getColor(): string|array|null
{
return match ($this) {
self::DRAFT => 'gray',
self::APPROVED => 'success',
self::PRODUCTION => 'primary',
self::SHIPPED => 'warning',
self::INVOICED => 'invoiced',
};
}
public function getIcon(): ?string
{
return match ($this) {
self::DRAFT => 'heroicon-o-pencil',
self::APPROVED => 'heroicon-o-check',
self::PRODUCTION => 'heroicon-o-arrow-path',
self::SHIPPED => 'heroicon-o-paper-airplane',
self::INVOICED => 'heroicon-o-credit-card',
};
}
}

@ -2,11 +2,18 @@
namespace App\Enums;
enum OrderType: string
use Filament\Support\Contracts\HasLabel;
enum OrderType: string implements HasLabel
{
case EMBROIDERY = 'Embroidery';
case SCREEN = 'Screen printing';
case DTG = 'Direct-to-garment';
case VINYL = 'Vinyl';
case MISC = 'Misc';
public function getLabel(): ?string
{
return $this->value;
}
}

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\ContactResource\Pages;
use App\Filament\Resources\ContactResource;
use Filament\Resources\Pages\CreateRecord;
class CreateContact extends CreateRecord
{
protected static string $resource = ContactResource::class;
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\ContactResource\Pages;
use App\Filament\Resources\ContactResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditContact extends EditRecord
{
protected static string $resource = ContactResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\ContactResource\Pages;
use App\Filament\Resources\ContactResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListContacts extends ListRecords
{
protected static string $resource = ContactResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

@ -0,0 +1,75 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\CustomerResource\Pages;
use App\Filament\Resources\CustomerResource\RelationManagers\ContactsRelationManager;
use App\Models\Customer;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class CustomerResource extends Resource
{
protected static ?string $model = Customer::class;
protected static ?string $navigationIcon = 'heroicon-o-building-office';
protected static ?string $navigationGroup = 'Management';
protected static ?int $navigationSort = 4;
public static function form(Form $form): Form
{
return $form
->schema([
Section::make([
TextInput::make('company_name'),
TextInput::make('phone'),
TextInput::make('shipping_address'),
TextInput::make('billing_address'),
])->columns(2),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('company_name'),
TextColumn::make('shipping_address'),
TextColumn::make('phone'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
ContactsRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListCustomers::route('/'),
'create' => Pages\CreateCustomer::route('/create'),
'edit' => Pages\EditCustomer::route('/{record}/edit'),
];
}
}

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\CustomerResource\Pages;
use App\Filament\Resources\CustomerResource;
use Filament\Resources\Pages\CreateRecord;
class CreateCustomer extends CreateRecord
{
protected static string $resource = CustomerResource::class;
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\CustomerResource\Pages;
use App\Filament\Resources\CustomerResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditCustomer extends EditRecord
{
protected static string $resource = CustomerResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\CustomerResource\Pages;
use App\Filament\Resources\CustomerResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListCustomers extends ListRecords
{
protected static string $resource = CustomerResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

@ -0,0 +1,55 @@
<?php
namespace App\Filament\Resources\CustomerResource\RelationManagers;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ContactsRelationManager extends RelationManager
{
protected static string $relationship = 'contacts';
public function form(Form $form): Form
{
return $form
->schema([
TextInput::make('first_name'),
TextInput::make('last_name'),
TextInput::make('email'),
TextInput::make('phone'),
TextInput::make('notes'),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('id')
->columns([
TextColumn::make('first_name'),
TextColumn::make('last_name'),
TextColumn::make('email'),
TextColumn::make('phone'),
TextColumn::make('notes'),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make(),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
// Tables\Actions\BulkActionGroup::make([
// Tables\Actions\DeleteBulkAction::make(),
// ]),
]);
}
}

@ -0,0 +1,64 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\InvoiceResource\Pages;
use App\Models\Invoice;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
class InvoiceResource extends Resource
{
protected static ?string $model = Invoice::class;
protected static ?string $navigationIcon = 'heroicon-o-clipboard-document-list';
protected static ?string $navigationGroup = 'Billing';
protected static ?int $navigationSort = 2;
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
//
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListInvoices::route('/'),
'create' => Pages\CreateInvoice::route('/create'),
'edit' => Pages\EditInvoice::route('/{record}/edit'),
];
}
}

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\InvoiceResource\Pages;
use App\Filament\Resources\InvoiceResource;
use Filament\Resources\Pages\CreateRecord;
class CreateInvoice extends CreateRecord
{
protected static string $resource = InvoiceResource::class;
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\InvoiceResource\Pages;
use App\Filament\Resources\InvoiceResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditInvoice extends EditRecord
{
protected static string $resource = InvoiceResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\InvoiceResource\Pages;
use App\Filament\Resources\InvoiceResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListInvoices extends ListRecords
{
protected static string $resource = InvoiceResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

@ -0,0 +1,182 @@
<?php
namespace App\Filament\Resources;
use App\Enums\OrderAttributes;
use App\Enums\OrderStatus;
use App\Enums\OrderType;
use App\Filament\Resources\OrderResource\Pages;
use App\Models\Contact;
use App\Models\Customer;
use App\Models\Order;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Split;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Components\Wizard;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class OrderResource extends Resource
{
protected static ?string $model = Order::class;
protected static ?string $navigationIcon = 'heroicon-o-shopping-bag';
protected static ?string $navigationGroup = 'Production';
public static function form(Form $form): Form
{
return $form->schema([
Wizard::make([
// Wizard\Step::make('Production Details')
// ->schema([
// Repeater::make('Garments')->schema([
// Split::make([
// TextInput::make('sku')->grow(true),
// TextInput::make('name')->grow(false),
// TextInput::make('color')->grow(true),
// ]),
// Split::make([
// TextInput::make('xs'),
// TextInput::make('s'),
// TextInput::make('m'),
// TextInput::make('l'),
// TextInput::make('xl'),
// TextInput::make('2xl'),
// TextInput::make('3xl'),
// TextInput::make('osfa'),
// ]),
// ])->grid(2),
// ]),
Wizard\Step::make('Order Details')->schema([
Section::make([
Select::make('order_type')
->required()
->options(OrderType::class)
->searchable(),
Split::make([
Select::make('customer_id')
->required()
->label('Customer')
->options(Customer::all()->pluck('company_name', 'id'))
->reactive()
->searchable(),
Select::make('contact_id')
->label('Contact')
->options(fn ($get): array => Contact::where('customer_id', $get('customer_id') ?? null)->get()->pluck('full_name', 'id')->toArray())
->searchable(),
]),
TextInput::make('customer_po')
->required()
->label('Customer PO'),
Split::make([
DatePicker::make('order_date')
->required()
->default(today()),
DatePicker::make('due_date')
->required()
->default(today()->add('10 days')),
]),
])->columnSpan(1),
Section::make([
ToggleButtons::make('status')
->required()
->options(OrderStatus::class)
->inline(),
ToggleButtons::make('order_attributes')
->options(OrderAttributes::class)
->multiple()
->inline(),
Textarea::make('notes')
->rows(3),
])->columnSpan(1),
])->columns(2),
// Wizard\Step::make('Production Details')
// ->schema([]),
])->columns(1),
])->columns(1);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('internal_po')
->label('Internal PO')
->copyable()
->fontFamily('mono')
->color('info')
->searchable()
->sortable(),
TextColumn::make('customer.company_name')
->searchable()
->sortable(),
TextColumn::make('customer_po')
->label('PO')
->wrap()
->copyable()
->weight('bold')
->color('code')
->searchable()
->sortable(),
TextColumn::make('order_date')
->searchable()
->sortable(),
TextColumn::make('due_date')
->searchable()
->sortable(),
TextColumn::make('status')
->badge()
->searchable()
->sortable(),
])
->defaultSort('order_date', 'desc')
->groups([
'status',
])
->filters([
Tables\Filters\SelectFilter::make('status')
->multiple()
->options(OrderStatus::class),
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListOrders::route('/'),
'create' => Pages\CreateOrder::route('/create'),
'edit' => Pages\EditOrder::route('/{record}/edit'),
];
}
}

@ -0,0 +1,12 @@
<?php
namespace App\Filament\Resources\OrderResource\Pages;
use App\Filament\Resources\OrderResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateOrder extends CreateRecord
{
protected static string $resource = OrderResource::class;
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\OrderResource\Pages;
use App\Filament\Resources\OrderResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditOrder extends EditRecord
{
protected static string $resource = OrderResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\OrderResource\Pages;
use App\Filament\Resources\OrderResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListOrders extends ListRecords
{
protected static string $resource = OrderResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

@ -0,0 +1,76 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\PackingSlipResource\Pages;
use App\Models\PackingSlip;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class PackingSlipResource extends Resource
{
protected static ?string $model = PackingSlip::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
protected static ?string $navigationGroup = 'Production';
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('date_received')
->sortable()
->searchable(),
TextColumn::make('order.customer_po')
->weight('bold')
->color('code')
->sortable()
->searchable(),
TextColumn::make('contents'),
TextColumn::make('amount'),
TextColumn::make('order.customer.company_name')
->sortable()
->searchable(),
])
->defaultSort('date_received', 'desc')
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPackingSlips::route('/'),
'create' => Pages\CreatePackingSlip::route('/create'),
'edit' => Pages\EditPackingSlip::route('/{record}/edit'),
];
}
}

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\PackingSlipResource\Pages;
use App\Filament\Resources\PackingSlipResource;
use Filament\Resources\Pages\CreateRecord;
class CreatePackingSlip extends CreateRecord
{
protected static string $resource = PackingSlipResource::class;
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\PackingSlipResource\Pages;
use App\Filament\Resources\PackingSlipResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditPackingSlip extends EditRecord
{
protected static string $resource = PackingSlipResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\PackingSlipResource\Pages;
use App\Filament\Resources\PackingSlipResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListPackingSlips extends ListRecords
{
protected static string $resource = PackingSlipResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

@ -0,0 +1,64 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\QuoteResource\Pages;
use App\Models\Quote;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
class QuoteResource extends Resource
{
protected static ?string $model = Quote::class;
protected static ?string $navigationIcon = 'heroicon-o-chat-bubble-bottom-center-text';
protected static ?string $navigationGroup = 'Billing';
protected static ?int $navigationSort = 1;
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
//
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListQuotes::route('/'),
'create' => Pages\CreateQuote::route('/create'),
'edit' => Pages\EditQuote::route('/{record}/edit'),
];
}
}

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\QuoteResource\Pages;
use App\Filament\Resources\QuoteResource;
use Filament\Resources\Pages\CreateRecord;
class CreateQuote extends CreateRecord
{
protected static string $resource = QuoteResource::class;
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\QuoteResource\Pages;
use App\Filament\Resources\QuoteResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditQuote extends EditRecord
{
protected static string $resource = QuoteResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\QuoteResource\Pages;
use App\Filament\Resources\QuoteResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListQuotes extends ListRecords
{
protected static string $resource = QuoteResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

@ -0,0 +1,33 @@
<?php
namespace App\Filament\Widgets;
use App\Enums\OrderStatus;
use App\Models\Order;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget as BaseWidget;
class LatestActiveOrders extends BaseWidget
{
protected static ?int $sort = 2;
public function table(Table $table): Table
{
return $table
->query(
Order::query()
->where('status', '!=', OrderStatus::SHIPPED)
->where('status', '!=', OrderStatus::INVOICED)
)
->columns([
TextColumn::make('customer.company_name'),
TextColumn::make('customer_po')
->color('code')
->weight('bold'),
TextColumn::make('status')
->badge(),
])
->defaultPaginationPageOption(5);
}
}

@ -0,0 +1,54 @@
<?php
namespace App\Filament\Widgets;
use App\Enums\OrderStatus;
use App\Models\Order;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
class OrderStats extends BaseWidget
{
// protected int|string|array $columnSpan = '2';
protected function getStats(): array
{
return [
Stat::make('Active Orders', $this->getActiveOrders())
->icon('heroicon-o-arrow-path')
->description('Orders that have yet to be completed'),
Stat::make('This Month', $this->getOrdersCurrentMonth())
->icon('heroicon-s-calendar')
->description('New orders since the beginning of the month'),
Stat::make('Due Today', $this->getDueOrders())
->icon('heroicon-o-clock')
->description('Orders that are scheduled to be due today'),
];
}
private function getActiveOrders(): string
{
return Order::all()
->where('order_status', '!=', OrderStatus::SHIPPED)
->where('order_status', '!=', OrderStatus::INVOICED)
->count();
}
private function getOrdersCurrentMonth(): string
{
return Order::all()
->where('order_status', '!=', OrderStatus::SHIPPED)
->where('order_status', '!=', OrderStatus::INVOICED)
->whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])
->count();
}
private function getDueOrders(): string
{
return Order::all()
->where('order_status', '!=', OrderStatus::SHIPPED)
->where('order_status', '!=', OrderStatus::INVOICED)
->where('due_date', '<=', now())
->count();
}
}

@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
class QuoteController extends Controller
{
//
}

@ -27,18 +27,13 @@ class Order extends Model
'order_type',
'status',
'due_date',
'rush',
'new_art',
'digitizing',
'repeat',
'event',
'purchased_garments',
'customer_supplied_file',
'notes',
'order_attributes',
];
protected $appends = [
'active',
protected $casts = [
'status' => OrderStatus::class,
'order_attributes' => 'array',
];
public static function boot(): void
@ -171,12 +166,13 @@ class Order extends Model
return $this->hasMany(ProductService::class);
}
public function packingSlips(): HasMany
{
return $this->hasMany(PackingSlip::class);
}
protected function serializeDate(DateTimeInterface $date): string
{
return $date->format('Y-m-d');
}
protected $casts = [
'status' => OrderStatus::class,
];
}

@ -28,4 +28,12 @@ class PackingSlip extends Model
{
return $this->belongsTo(Customer::class);
}
/**
* @return BelongsTo<Order, self>)
*/
public function order(): BelongsTo
{
return $this->belongsTo(Order::class);
}
}

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Quote extends Model
{
use HasFactory;
}

@ -0,0 +1,60 @@
<?php
namespace App\Providers\Filament;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Blue,
'code' => Color::hex('#d63384'),
'invoiced' => Color::hex('#900090'),
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
// Widgets\AccountWidget::class,
// Widgets\FilamentInfoWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
])
->sidebarWidth('12rem');
}
}

@ -2,4 +2,5 @@
return [
App\Providers\AppServiceProvider::class,
App\Providers\Filament\AdminPanelProvider::class,
];

@ -7,6 +7,7 @@
"require": {
"php": "^8.2",
"davidhsianturi/blade-bootstrap-icons": "^1.5",
"filament/filament": "^3.2",
"laravel/framework": "^11.9",
"laravel/tinker": "^2.9",
"livewire/livewire": "^3.5",
@ -37,7 +38,8 @@
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
"@php artisan package:discover --ansi",
"@php artisan filament:upgrade"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"

2304
composer.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,89 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Broadcasting
|--------------------------------------------------------------------------
|
| By uncommenting the Laravel Echo configuration, you may connect Filament
| to any Pusher-compatible websockets server.
|
| This will allow your users to receive real-time notifications.
|
*/
'broadcasting' => [
// 'echo' => [
// 'broadcaster' => 'pusher',
// 'key' => env('VITE_PUSHER_APP_KEY'),
// 'cluster' => env('VITE_PUSHER_APP_CLUSTER'),
// 'wsHost' => env('VITE_PUSHER_HOST'),
// 'wsPort' => env('VITE_PUSHER_PORT'),
// 'wssPort' => env('VITE_PUSHER_PORT'),
// 'authEndpoint' => '/broadcasting/auth',
// 'disableStats' => true,
// 'encrypted' => true,
// 'forceTLS' => true,
// ],
],
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| This is the storage disk Filament will use to store files. You may use
| any of the disks defined in the `config/filesystems.php`.
|
*/
'default_filesystem_disk' => env('FILAMENT_FILESYSTEM_DISK', 'public'),
/*
|--------------------------------------------------------------------------
| Assets Path
|--------------------------------------------------------------------------
|
| This is the directory where Filament's assets will be published to. It
| is relative to the `public` directory of your Laravel application.
|
| After changing the path, you should run `php artisan filament:assets`.
|
*/
'assets_path' => null,
/*
|--------------------------------------------------------------------------
| Cache Path
|--------------------------------------------------------------------------
|
| This is the directory that Filament will use to store cache files that
| are used to optimize the registration of components.
|
| After changing the path, you should run `php artisan filament:cache-components`.
|
*/
'cache_path' => base_path('bootstrap/cache/filament'),
/*
|--------------------------------------------------------------------------
| Livewire Loading Delay
|--------------------------------------------------------------------------
|
| This sets the delay before loading indicators appear.
|
| Setting this to 'none' makes indicators appear immediately, which can be
| desirable for high-latency connections. Setting it to 'default' applies
| Livewire's standard 200ms delay.
|
*/
'livewire_loading_delay' => 'default',
];

@ -14,25 +14,19 @@ class OrderFactory extends Factory
public function definition(): array
{
$order_date = Carbon::today()->subDays(rand(0, 10));
$order_date = Carbon::today()->subDays(rand(0, 30));
$due_date = $order_date->copy()->addDays(rand(9, 15));
return [
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
'customer_po' => $this->faker->randomNumber(6, true),
'order_type' => $this->faker->randomElement(OrderType::cases())->value,
'order_date' => $order_date,
'due_date' => $due_date,
'status' => $this->faker->randomElement(OrderStatus::cases())->value,
'rush' => $this->faker->boolean(10),
'event' => $this->faker->boolean(),
'new_art' => $this->faker->boolean(),
'digitizing' => $this->faker->boolean(),
'repeat' => $this->faker->boolean(),
'purchased_garments' => $this->faker->boolean(),
'customer_supplied_file' => $this->faker->boolean(),
'notes' => $this->faker->words(10, true),
'created_at' => $order_date,
'updated_at' => Carbon::now(),
'customer_po' => $this->faker->randomNumber(6, true),
'order_type' => $this->faker->randomElement(OrderType::cases())->value,
'order_date' => $order_date,
'due_date' => $due_date,
'status' => $this->faker->randomElement(OrderStatus::cases())->value,
'notes' => $this->faker->words(10, true),
'order_attributes' => ['New Art', 'Garments', 'Rush'],
];
}
}

@ -18,14 +18,8 @@ return new class extends Migration
$table->date('order_date');
$table->date('due_date');
$table->string('status');
$table->boolean('rush')->default(0);
$table->boolean('event')->default(0);
$table->boolean('new_art')->default(0);
$table->boolean('digitizing')->default(0);
$table->boolean('repeat')->default(0);
$table->boolean('purchased_garments')->default(0);
$table->boolean('customer_supplied_file')->default(0);
$table->longText('notes')->nullable();
$table->json('order_attributes');
$table->softDeletes();
$table->timestamps();
});

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('quotes', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('quotes');
}
};

@ -20,14 +20,14 @@ class CustomerSeeder extends Seeder
for ($i = 0; $i < 5; $i++) {
Customer::factory()
->has(Contact::factory(rand(1, 5)))
->has(PackingSlip::factory(rand(1, 10)))
->has(ShippingEntry::factory(rand(1, 3)))
->has(Order::factory(rand(2, 20))
->has(OrderProduct::factory(rand(1, 10))
->has(productSize::factory(rand(1, 8))))
->has(ProductService::factory(rand(1, 10), [
->has(Order::factory(rand(5, 10))
->has(OrderProduct::factory(rand(1, 3))
->has(productSize::factory(rand(1, 3))))
->has(ProductService::factory(rand(1, 5), [
'service_file_id' => ServiceFile::factory(),
])))
]))
->has(PackingSlip::factory(rand(0, 2))))
->create();
}

@ -0,0 +1 @@
Subproject commit 65fe7885813f3d4dc87f0fc52495247592fe276c

1377
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -7,10 +7,13 @@
},
"devDependencies": {
"@popperjs/core": "^2.11.6",
"autoprefixer": "^10.4.20",
"axios": "^1.6.4",
"bootstrap": "^5.2.3",
"laravel-vite-plugin": "^1.0",
"postcss": "^8.4.47",
"sass": "^1.56.1",
"tailwindcss": "^3.4.13",
"vite": "^5.0"
},
"dependencies": {

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,21 @@
{
"node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff": {
"file": "assets/bootstrap-icons-BOrJxbIo.woff",
"src": "node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff"
},
"node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff2": {
"file": "assets/bootstrap-icons-BtvjY1KL.woff2",
"src": "node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff2"
},
"resources/js/app.js": {
"file": "assets/app-L09tcoah.js",
"name": "app",
"src": "resources/js/app.js",
"isEntry": true
},
"resources/sass/app.scss": {
"file": "assets/app-Dgcz2HWl.css",
"src": "resources/sass/app.scss",
"isEntry": true
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
function r({state:o}){return{state:o,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{["x-on:blur"]:"createTag()",["x-model"]:"newTag",["x-on:keydown"](t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},["x-on:paste"](){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default};

@ -0,0 +1 @@
function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init:function(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight:function(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize:function(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver:function(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
function n(){return{checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,init:function(){this.livewireId=this.$root.closest("[wire\\:id]").attributes["wire:id"].value,this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1}),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction:function(e,t=null){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableAction(e,t)},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){if(this.isLoading=!0,this.areRecordsSelected(this.getRecordsInGroupOnPage(e))){this.deselectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e));return}this.selectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e)),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let t=[];for(let s of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])s.dataset.group===e&&t.push(s.value);return t},getRecordsOnPage:function(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords:function(e){for(let t of e)this.isRecordSelected(t)||this.selectedRecords.push(t)},deselectRecords:function(e){for(let t of e){let s=this.selectedRecords.indexOf(t);s!==-1&&this.selectedRecords.splice(s,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(t=>this.isRecordSelected(t))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]},watchForCheckboxClicks:function(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick:function(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let s=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!s.includes(this.lastChecked)){this.lastChecked=t;return}let o=s.indexOf(this.lastChecked),r=s.indexOf(t),l=[o,r].sort((i,d)=>i-d),c=[];for(let i=l[0];i<=l[1];i++)s[i].checked=t.checked,c.push(s[i].value);t.checked?this.selectRecords(c):this.deselectRecords(c)}this.lastChecked=t}}}export{n as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

@ -0,0 +1,3 @@
/*@tailwind base;*/
/*@tailwind components;*/
/*@tailwind utilities;*/

@ -14,7 +14,7 @@
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
<!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
@vite(['resources/sass/app.scss', 'resources/css/app.css', 'resources/js/app.js'])
</head>
<body>

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./resources/**/*.blade.php",
"./app/Filament/**/*.blade.php"
],
theme: {
extend: {},
},
plugins: [],
}
Loading…
Cancel
Save