Compare commits

..

28 Commits

Author SHA1 Message Date
d2c72a3f33 WIP #129 Product count sum 2025-04-11 17:31:13 -04:00
3dc4b98959 #148: Change amount label to unit price on order form 2025-04-11 16:29:25 -04:00
b6592d7945 #134: Fix Dashboard 'in the past 30 days' 2025-04-11 16:27:41 -04:00
bc63a4074b #147: Resource Lock sidebar settings 2025-04-11 16:27:41 -04:00
2d5688b66e #132: Inches / cm toggle for order page
Also shows size unit on PDFs
2025-04-11 16:27:41 -04:00
f02fb63bfd Merge branch 'main' into development 2025-04-10 18:08:57 +02:00
6fdf471463 #145: Default order sort is not right 2025-04-10 12:08:28 -04:00
6be8998e3a Pint fixes 2025-04-10 11:59:06 -04:00
671a2950c7 Squash merge fix into main 2025-04-10 11:41:46 -04:00
2461a4457a Merge pull request 'Version 20250408' (#143) from development into main
Reviewed-on: #143
2025-04-08 19:49:17 +02:00
e898f3d884 #142 Packing Slip content field should be required 2025-04-08 13:22:47 -04:00
a188c34409 #136: Add new filters to order list 2025-04-08 13:03:04 -04:00
081be2f930 #138: Add decoration type to order list 2025-04-08 12:45:48 -04:00
153014dba8 #130: Improve service colors on printed PDFs 2025-04-07 20:19:12 -04:00
51f21f94c1 #135: Add create customer modal to packing slip page 2025-04-07 20:02:31 -04:00
4eb94a736a Fix InvoiceTest and add placeholder file for Feature folder 2025-04-07 19:52:21 -04:00
07c250eaac Fix product name nullability in migration 2025-04-07 19:44:04 -04:00
4500ce0c27 Merge pull request 'development' (#139) from development into main
Reviewed-on: #139
2025-04-08 01:38:08 +02:00
f188063967 Disable workflow 2025-04-07 19:33:45 -04:00
bcd5c6cdef #133: Make product name not required 2025-04-07 11:07:14 -04:00
86e48f8e16 #127 Implement Resource Lock 2025-04-03 16:27:46 -04:00
63d5f427ad #125: 'Add Payment' on List Invoice page modal is too wide 2025-04-03 11:58:16 -04:00
5de1726ce8 #124: Product Service reorder button is on wrong side 2025-04-03 11:55:21 -04:00
01f602f8d6 #123: Typo 'Add to product Services' 2025-04-03 11:52:04 -04:00
320fc0cc98 #122: Non-admins can see payments in customer edit page 2025-04-03 11:52:04 -04:00
4e952f96c0 add local env file
Some checks failed
Deploy / deploy (push) Failing after 9s
2025-04-02 15:08:10 -04:00
43eebd9528 Merge branch 'development'
Some checks failed
Deploy / deploy (push) Failing after 8s
2025-03-11 12:52:46 -04:00
3defbd8253 Merge pull request 'Version 20250311' (#121) from development into main
Some checks failed
Deploy / deploy (push) Failing after 6s
Reviewed-on: nisse/topnotch_website#121
2025-03-11 17:33:40 +01:00
29 changed files with 492 additions and 69 deletions

View File

@ -1,9 +1,9 @@
APP_NAME=Laravel
APP_NAME="Top Notch"
APP_ENV=local
APP_KEY=
APP_KEY=base64:vRgghlbIdXQxXIEvgUArbI9FURhgdyqx3LDXDwHYSmA=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_URL=localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
@ -19,12 +19,12 @@ LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=sail
DB_PASSWORD=password
SESSION_DRIVER=database
SESSION_LIFETIME=120
@ -42,13 +42,13 @@ CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
@ -62,3 +62,8 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://meilisearch:7700
MEILISEARCH_NO_ANALYTICS=false

View File

@ -1,4 +1,18 @@
# Changelog
**2025-04-11**
- Fixed #132 - Inches / cm toggle for order page (and show unit on pdf)
**2025-04-10**
- Fixed #145 - Default order sort is incorrect (newest will always show on top now)
- Fixed #142 - Packing Slip content field should be required
**2025-04-08**
- Added #138 - Add Decoration method to order list
- Fixed #133 - Make product name not required
- Fixed #127 - Implement resource lock for orders
- Fixed #125 - 'Add Payment' on LIst Invoice page modal is too wide
- Fixed #124 - Reorder button for product services is displayed on wrong side
- Fixed #123 - Typo 'Add to product Services'
**2025-03-11**
- Fixed #122 - Non-admins can see payments

View File

@ -2,9 +2,10 @@
namespace App\Enums;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasLabel;
enum OrderType: string implements HasLabel
enum OrderType: string implements HasColor, HasLabel
{
case EMB = 'embroidery';
case SCP = 'screen_printing';
@ -22,4 +23,15 @@ public function getLabel(): string
self::MISC => 'Misc',
};
}
public function getColor(): string
{
return match ($this) {
self::EMB => 'embroidery',
self::SCP => 'screen_printing',
self::DTG => 'primary',
self::VINYL => 'vinyl',
self::MISC => 'misc',
};
}
}

View File

@ -249,6 +249,7 @@ public static function table(Table $table): Table
BulkAction::make('Create Payment')
->icon(IconEnum::PAYMENTS->value)
->form(fn ($form) => PaymentResource::form($form))
->modalWidth('lg')
->action(function (Collection $records, array $data) {
if ($records->pluck('customer_id')->unique()->count() !== 1) {
Notification::make()

View File

@ -33,12 +33,29 @@
use Filament\Tables\Actions\BulkActionGroup;
use Filament\Tables\Columns\IconColumn\IconColumnSize;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Guava\FilamentClusters\Forms\Cluster;
use Icetalker\FilamentTableRepeater\Forms\Components\TableRepeater;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
function recalculateRowTotal(callable $set, callable $get): void
{
$amounts = collect([
$get('xs'),
$get('s'),
$get('m'),
$get('l'),
$get('xl'),
$get('2xl'),
$get('3xl'),
$get('osfa'),
])->map(fn ($val) => floatval($val ?? 0));
$set('total', $amounts->sum());
}
class OrderResource extends Resource
{
protected static ?string $model = Order::class;
@ -51,6 +68,8 @@ public static function form(Form $form): Form
{
return $form->schema([
Group::make()
->columns(6)
->columnSpan(2)
->schema([
Section::make([
Grid::make(1)
@ -139,55 +158,82 @@ public static function form(Form $form): Form
->columnSpan(1)
->hidden(fn (?Order $record) => $record === null)
->extraAttributes(['class' => 'h-full']),
])
->columns(6)
->columnSpan(2),
]),
TableRepeater::make('order_products')
->label('Garments')
->addActionLabel('Add new garment')
->reorderable()
->cloneable()
->defaultItems(1)
->schema([
TextInput::make('sku')
->datalist(OrderProduct::all()->unique('sku')->pluck('sku')->toArray()),
TextInput::make('product_name')
->datalist(OrderProduct::all()->unique('product_name')->pluck('product_name')->toArray())
->required(),
->datalist(OrderProduct::all()->unique('product_name')->pluck('product_name')->toArray()),
TextInput::make('color')
->datalist(OrderProduct::all()->unique('color')->pluck('color')->toArray()),
Cluster::make([
TextInput::make('xs')
->placeholder('xs')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('s')
->placeholder('s')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('m')
->placeholder('m')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('l')
->placeholder('l')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('xl')
->placeholder('xl')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('2xl')
->placeholder('2xl')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('3xl')
->placeholder('3xl')
->rules('numeric'),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
TextInput::make('osfa')
->placeholder('osfa')
->rules('numeric'),
])
->label('Sizes'),
])
->reorderable()
->cloneable()
->defaultItems(1),
->rules('numeric')
->reactive()
->afterStateUpdated(fn ($state, callable $set, callable $get) => recalculateRowTotal($set, $get)),
])->label('Sizes'),
TextInput::make('total')
->disabled()
->dehydrated(false)
->afterStateHydrated(fn (callable $get, callable $set) => recalculateRowTotal($set, $get)),
]),
Repeater::make('services')
->view('filament.forms.compact-repeater')
->label('Product Services')
->addActionLabel('Add new product service')
->schema([
Grid::make(19)
->schema([
@ -228,34 +274,44 @@ public static function form(Form $form): Form
->prefix('h')
->rules('numeric'),
])
->label('Dimensions (inches)')
->label('Dimensions')
->columnSpan(4),
TextInput::make('amount')
->label('Quantity')
->live()
->prefix('#')
->columnSpan(2)
->rules('numeric'),
TextInput::make('amount_price')
->label('Amount')
->label('Unit price')
->prefix('$')
->columnSpan(2)
->rules('numeric'),
]),
Grid::make(9)
Grid::make(19)
->schema([
TextInput::make('serviceFileCode')
->label('Code')
->datalist(ServiceFile::all()->unique('code')->pluck('code')->toArray())
->columnSpan(1)
->columnSpan(2)
->placeholder('A1234'),
Select::make('serviceFileSizeUnit')
->columnSpan(2)
->selectablePlaceholder(false)
->label('Unit')
->options([
'in' => 'Inches',
'cm' => 'cm',
]),
Textarea::make('notes')
->placeholder('Thread colors...')
->columnSpan(8),
->placeholder('Thread colors... (press enter for new line)')
->autosize()
->rows(1)
->columnSpan(15),
]),
])
->reorderable()
@ -298,6 +354,11 @@ public static function table(Table $table): Table
})
->sortable(),
TextColumn::make('order_type')
->badge()
->searchable()
->sortable(),
TextColumn::make('customer.company_name')
->searchable()
->sortable(),
@ -326,8 +387,16 @@ public static function table(Table $table): Table
->searchable()
->sortable(),
])
->defaultSort('order_date', 'desc')
->defaultSort('id', 'desc')
->filters([
SelectFilter::make('order_type')
->options(OrderType::class)
->multiple(),
SelectFilter::make('status')
->options(OrderStatus::class)
->multiple(),
Tables\Filters\Filter::make('order_date')
->form([
DatePicker::make('created_from'),

View File

@ -63,6 +63,7 @@ protected function handleRecordCreation(array $data): Order
'width' => $service['serviceFileWidth'] ?? null,
'height' => $service['serviceFileHeight'] ?? null,
'setup_number' => $service['serviceFileSetupNumber'] ?? null,
'size_unit' => $service['serviceFileSizeUnit'] ?? 'inch',
]);
ProductService::create([

View File

@ -21,9 +21,12 @@
use Filament\Resources\Pages\EditRecord;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Database\Eloquent\Model;
use Kenepa\ResourceLock\Resources\Pages\Concerns\UsesResourceLock;
class EditOrder extends EditRecord
{
use UsesResourceLock;
protected static string $resource = OrderResource::class;
public function getTitle(): string|Htmlable
@ -65,6 +68,7 @@ protected function mutateFormDataBeforeFill(array $data): array
'serviceFileHeight' => $service->serviceFile->height ?? '',
'serviceFileCode' => $service->serviceFile->code ?? '',
'serviceFileSetupNumber' => $service->serviceFile->setup_number ?? '',
'serviceFileSizeUnit' => $service->serviceFile->size_unit ?? 'inch',
];
}
@ -136,6 +140,7 @@ public function handleRecordUpdate(Model $record, array $data): Model
'width' => $service['serviceFileWidth'] ?? null,
'height' => $service['serviceFileHeight'] ?? null,
'setup_number' => $service['serviceFileSetupNumber'] ?? null,
'size_unit' => $service['serviceFileSizeUnit'] ?? 'inch',
]);
ProductService::create([

View File

@ -10,11 +10,17 @@
use Filament\Actions;
use Filament\Resources\Components\Tab;
use Filament\Resources\Pages\ListRecords;
use Filament\Support\Enums\MaxWidth;
class ListOrders extends ListRecords
{
protected static string $resource = OrderResource::class;
public function getMaxContentWidth(): string
{
return 'max-w-[1920px]';
}
private function excludeStatuses($query): mixed
{
return $query

View File

@ -3,7 +3,6 @@
namespace App\Filament\Admin\Resources;
use App\Enums\IconEnum;
use App\Models\Customer;
use App\Models\Order;
use App\Models\PackingSlip;
use Filament\Forms\Components\DatePicker;
@ -39,11 +38,16 @@ public static function form(Form $form): Form
DatePicker::make('date_received')
->default(today())
->required(),
Select::make('customer_id')
->label('Customer')
->options(Customer::all()->pluck('company_name', 'id'))
->relationship(name: 'customer', titleAttribute: 'company_name')
->preload()
->createOptionForm(fn ($form) => CustomerResource::form($form))
->createOptionAction(fn ($action) => $action->modalWidth('lg'))
->reactive()
->searchable(),
Select::make('order_id')
->label('Order')
->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null)
@ -57,6 +61,7 @@ public static function form(Form $form): Form
Grid::make(1)
->schema([
TextArea::make('contents')
->required()
->rows(9),
])
->columnSpan(1),
@ -73,7 +78,6 @@ public static function table(Table $table): Table
->sortable()
->searchable(),
TextColumn::make('order.customer_po')
// ->url(fn ($record) => $record->to)
->url(fn ($record) => OrderResource::getUrl('edit', ['record' => $record->id]))
->weight('bold')
->color('code')

View File

@ -49,7 +49,7 @@ private function getOrdersPast30Days(): string
->where('order_status', '!=', OrderStatus::READY_FOR_INVOICE)
->where('order_status', '!=', OrderStatus::SHIPPED)
->where('order_status', '!=', OrderStatus::INVOICED)
->whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])
->whereBetween('created_at', [now()->subDays(30), now()])
->count();
}

View File

@ -16,13 +16,14 @@
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Carbon;
use Kenepa\ResourceLock\Models\Concerns\HasLocks;
#[ObservedBy(OrderObserver::class)]
class Order extends Model
{
/** @use HasFactory<OrderFactory> */
use HasFactory, SoftDeletes;
use HasFactory, HasLocks, SoftDeletes;
protected $fillable = [
'customer_id',

View File

@ -26,6 +26,7 @@ class ProductService extends Model
'amount',
'amount_price',
'notes',
'size_unit',
];
protected $appends = [

View File

@ -19,6 +19,7 @@ class ServiceFile extends Model
'width',
'height',
'setup_number',
'size_unit',
];
/**

View File

@ -18,6 +18,7 @@
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Kenepa\ResourceLock\ResourceLockPlugin;
class AdminPanelProvider extends PanelProvider
{
@ -29,10 +30,14 @@ public function panel(Panel $panel): Panel
->path('admin')
->login(UsernameLogin::class)
->colors([
'primary' => Color::Blue,
'code' => Color::hex('#d63384'),
'invoicing' => Color::hex('#DD00DD'),
'invoiced' => Color::hex('#900090'),
'primary' => Color::Blue,
'code' => Color::hex('#d63384'),
'invoicing' => Color::hex('#DD00DD'),
'invoiced' => Color::hex('#900090'),
'embroidery' => Color::hex('#FF00FF'),
'screen_printing' => Color::hex('#0088FF'),
'vinyl' => Color::hex('#22AA22'),
'misc' => Color::hex('#0000FF'),
])
->discoverResources(in: app_path('Filament/Admin/Resources/'), for: 'App\\Filament\\Admin\\Resources')
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
@ -62,6 +67,7 @@ public function panel(Panel $panel): Panel
NavigationGroup::make('Financial'),
NavigationGroup::make('Reports'),
NavigationGroup::make('Settings'),
]);
])
->plugin(ResourceLockPlugin::make());
}
}

View File

@ -7,15 +7,17 @@
"require": {
"php": "^8.2",
"davidhsianturi/blade-bootstrap-icons": "^1.5",
"doctrine/dbal": "^4.2",
"fakerphp/faker": "^1.23",
"filament/filament": "^3.2",
"guava/filament-clusters": "^1.4",
"icetalker/filament-table-repeater": "^1.3",
"kenepa/resource-lock": "^2.1",
"laravel/framework": "^11.9",
"laravel/tinker": "^2.9",
"livewire/livewire": "^3.5",
"mallardduck/blade-lucide-icons": "^1.23",
"spatie/laravel-pdf": "^1.5",
"fakerphp/faker": "^1.23"
"spatie/laravel-pdf": "^1.5"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^3.5",

70
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "46ac06345fc3d57a7d0a01cfcaeaac17",
"content-hash": "139b4d2b0a40ace1cf1efaf4c7facf20",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@ -2238,6 +2238,74 @@
],
"time": "2025-03-05T16:06:45+00:00"
},
{
"name": "kenepa/resource-lock",
"version": "2.1.3",
"source": {
"type": "git",
"url": "https://github.com/kenepa/resource-lock.git",
"reference": "dda6e14b5c4a47817569081b11248c572ebdfd07"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kenepa/resource-lock/zipball/dda6e14b5c4a47817569081b11248c572ebdfd07",
"reference": "dda6e14b5c4a47817569081b11248c572ebdfd07",
"shasum": ""
},
"require": {
"filament/filament": "^3.0",
"illuminate/contracts": "^9.0|^10.0|^11.0|^12.0",
"php": "^8.1",
"spatie/laravel-package-tools": "^1.15.0"
},
"require-dev": {
"laravel/pint": "^1.0",
"nunomaduro/collision": "^7.0|^8.1",
"nunomaduro/larastan": "^2.0.1",
"orchestra/testbench": "^7.0|^8.0|^9.0|^10.0",
"pestphp/pest": "^2.0|^3.7",
"pestphp/pest-plugin-laravel": "^2.0|^3.1",
"pestphp/pest-plugin-livewire": "^2.0|^3.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0|^2.0",
"phpstan/phpstan-phpunit": "^1.0|^2.0",
"spatie/laravel-ray": "^1.26",
"tightenco/duster": "^1.1|^3.1"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"ResourceLock": "Kenepa\\ResourceLock\\Facades\\ResourceLock"
},
"providers": [
"Kenepa\\ResourceLock\\ResourceLockServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Kenepa\\ResourceLock\\": "src",
"Kenepa\\ResourceLock\\Database\\Factories\\": "database/factories"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Filament Resource Lock is a Filament plugin that adds resource locking functionality to your site.",
"homepage": "https://github.com/kenepa/resource-lock",
"keywords": [
"Kenepa",
"laravel",
"resource-lock"
],
"support": {
"issues": "https://github.com/kenepa/resource-lock/issues",
"source": "https://github.com/kenepa/resource-lock/tree/2.1.3"
},
"time": "2025-03-14T16:31:20+00:00"
},
{
"name": "kirschbaum-development/eloquent-power-joins",
"version": "4.2.1",

View File

@ -25,7 +25,7 @@
| or any other location as required by the application or its packages.
*/
'version' => '20250311',
'version' => '20250411',
/*
|--------------------------------------------------------------------------

130
config/resource-lock.php Normal file
View File

@ -0,0 +1,130 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Models
|--------------------------------------------------------------------------
|
| The models configuration specifies the classes that represent your application's
| data objects. This configuration is used by the framework to interact with
| the application's data models. You can even implement your own ResourceLock model.
|
*/
'models' => [
'User' => \App\Models\User::class,
// 'ResourceLock' => null,
],
/*
|--------------------------------------------------------------------------
| Filament Resource
|--------------------------------------------------------------------------
|
| The resource lock filament resource displays all the current locks in place.
| You are able to replace the resource Lock with your own resource class.
|
*/
'resource' => [
'class' => \Kenepa\ResourceLock\Resources\LockResource::class,
],
/*
|--------------------------------------------------------------------------
| Resource Unlocker Button
|--------------------------------------------------------------------------
|
| The unlocker configuration specifies whether limited access is enabled for
| the resource unlock button. If limited access is enabled, only specific
| users or roles will be able to unlock locked resources directly from
| the modal.
|
*/
'unlocker' => [
'limited_access' => false,
// 'gate' => ''
],
/*
|--------------------------------------------------------------------------
| Lock Notice
|--------------------------------------------------------------------------
|
| The lock notice contains several configuration options for the modal
| that is display when a resource is locked.
|
*/
'lock_notice' => [
'display_resource_lock_owner' => false,
],
/*
|--------------------------------------------------------------------------
| Resource Lock Manager
|--------------------------------------------------------------------------
|
| The resource lock manager provides a simple way to view all resource locks
| of your application. It provides several ways to quickly unlock all or
| specific resources within your app.
|
*/
'manager' => [
'navigation_badge' => false,
'navigation_icon' => 'heroicon-o-lock-closed',
'navigation_label' => 'Resource Locks',
'plural_label' => 'Resource Locks',
'navigation_group' => 'Settings',
'navigation_sort' => 200,
'limited_access' => false,
'should_register_navigation' => true,
// 'gate' => ''
],
/*
|--------------------------------------------------------------------------
| Lock timeout (in minutes)
|--------------------------------------------------------------------------
|
| The lock_timeout configuration specifies the time interval, in minutes,
| after which a lock on a resource will expire if it has not been manually
| unlocked or released by the user.
|
*/
'lock_timeout' => 10,
/*
|--------------------------------------------------------------------------
| Check Locks before saving
|--------------------------------------------------------------------------
|
| The check_locks_before_saving configuration specifies whether a lock of a resource will be checked
| before saving a resource if a tech-savvy user is able to bypass the locked
| resource modal and attempt to save the resource. In some cases you may want to turns this off.
| It's recommended to keep this on.
|
*/
'check_locks_before_saving' => true,
/*
|--------------------------------------------------------------------------
| Actions
|--------------------------------------------------------------------------
|
| Action classes are simple classes that execute some logic within the package.
| If you want to add your own custom logic you are able to extend your own
| class with class your overwriting.
| Learn more about action classes: https://freek.dev/2442-strategies-for-making-laravel-packages-customizable
|
*/
'actions' => [
'get_resource_lock_owner_action' => \Kenepa\ResourceLock\Actions\GetResourceLockOwnerAction::class,
],
];

View File

@ -19,7 +19,6 @@ public function definition(): array
'name' => $this->faker->word(),
'width' => round($this->faker->randomFloat(2, 0, 10), 1),
'height' => round($this->faker->randomFloat(2, 0, 10), 1),
'unit' => 'inch',
];
}
}

View File

@ -0,0 +1,29 @@
<?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('resource_locks', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('user_id')->constrained();
$table->morphs('lockable');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('resource_locks');
}
};

View File

@ -0,0 +1,28 @@
<?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::table('order_products', function (Blueprint $table) {
$table->string('product_name')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('order_products', function (Blueprint $table) {
$table->string('product_name')->nullable(false)->change();
});
}
};

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('service_files', function (Blueprint $table) {
$table->string('size_unit')->default('in');
});
}
public function down(): void
{
Schema::table('service_files', function (Blueprint $table) {
$table->dropColumn('size_unit');
});
}
};

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "topnotch_website",
"name": "order_system",
"lockfileVersion": 3,
"requires": true,
"packages": {

View File

@ -118,6 +118,7 @@ class="fi-fo-repeater-item divide-y divide-gray-100 rounded-xl bg-white shadow-
>
@if ($reorderActionIsVisible || $moveUpActionIsVisible || $moveDownActionIsVisible)
<ul class="flex items-center gap-x-3">
{{--
@if ($reorderActionIsVisible)
<li
x-sortable-handle
@ -126,6 +127,7 @@ class="fi-fo-repeater-item divide-y divide-gray-100 rounded-xl bg-white shadow-
{{ $reorderAction }}
</li>
@endif
--}}
@if ($moveUpActionIsVisible || $moveDownActionIsVisible)
<li
@ -156,7 +158,7 @@ class="flex items-center justify-center"
</h4>
@endif
@if ($cloneActionIsVisible || $deleteActionIsVisible || $isCollapsible || $visibleExtraItemActions)
@if ($cloneActionIsVisible || $deleteActionIsVisible || $isCollapsible || $visibleExtraItemActions || $reorderActionIsVisible)
<ul
class="ms-auto flex items-center gap-x-3"
>
@ -166,6 +168,15 @@ class="ms-auto flex items-center gap-x-3"
</li>
@endforeach
@if ($reorderActionIsVisible)
<li
x-sortable-handle
x-on:click.stop
>
{{ $reorderAction }}
</li>
@endif
@if ($cloneActionIsVisible)
<li x-on:click.stop>
{{ $cloneAction }}

View File

@ -111,7 +111,9 @@
</div>
<div class="fs-6 text-primary">
{{$order->customer_po}}
@if($order->pre_production)<span class="text-danger"> (pre-pro)</span>@endif()
@if($order->pre_production)
<span class="text-danger"> (pre-pro)</span>
@endif()
</div>
</div>
@ -153,7 +155,7 @@
<tbody>
@foreach($order->orderProducts as $product)
<tr>
<td><code>{{$product->sku}}</code></td>
<td><code style="font-size: 0.9rem;">{{$product->sku}}</code></td>
<td class="text-uppercase">{{$product->product_name}}</td>
<td class="text-uppercase">{{$product->color}}</td>
<td style="width: 40px;">{{$product->productSizes()->get()->where('size', 'xs')->first()->amount ?? ''}}</td>
@ -182,12 +184,13 @@
<!-- Services Table -->
<div class="row mt-2">
<table class="table table-striped" style="font-size: 12px;">
<table class="table table-striped" style="font-size: 13px;">
<thead class="opacity-50 fw-normal">
<th>Placement & Service</th>
<th class="w-50">Logo Name & Instructions</th>
<th>Width</th>
<th>Height</th>
<th>Unit</th>
<th>QTY</th>
</thead>
@ -200,18 +203,18 @@
</div>
<br>
<div class="text-uppercase">
<code style="font-size: 12px">
{{$service->serviceFile->code}}
<code style="font-size: 14px">
{{$service->serviceFile->code ?? ''}}
</code>
</div>
</td>
<td>
<div class="text-uppercase">
{{$service->serviceFile->name}}
{{$service->serviceFile->name ?? ''}}
</div>
<br>
<div class="text-uppercase">
{{$service->notes}}
<div class="text-uppercase" style="color: #d63384;">
{{$service->notes ?? ''}}
</div>
</td>
<td>
@ -220,6 +223,9 @@
<td>
{{$service->serviceFile->height}}
</td>
<td>
{{$service->serviceFile->size_unit}}
</td>
<td>
{{$service->amount}}
</td>

View File

@ -6,9 +6,11 @@ export default {
"./app/Filament/**/*.php",
"./vendor/filament/**/*.php"
],
safelist: [
'max-w-[1920px]'
],
theme: {
extend: {},
},
plugins: [],
}

View File

View File

@ -54,7 +54,7 @@
->assertHasNoErrors();
$this->assertDatabaseHas('invoices', [
'internal_id' => 'TN40001',
'internal_id' => 'TN4001',
'customer_id' => $formData['customer_id'],
'status' => $formData['status'],
'has_gst' => $formData['has_gst'],
@ -65,7 +65,7 @@
'hst_rate' => $hst_rate,
]);
$invoice = Invoice::where('internal_id', 'TN40001')->firstOrFail();
$invoice = Invoice::where('internal_id', 'TN4001')->firstOrFail();
$this->assertEquals($invoice->orders->isEmpty(), true);
});