Getting Started
Admin Panel
Search for a command to run...
Understanding Deeplancer's architecture and development patterns
Understanding how Deeplancer is structured helps you extend it effectively. This isn't a Laravel tutorial—we assume you know the framework. Here's how this project works.
Deeplancer uses a strict namespace and directory structure:
Admin Components:
App\Livewire\Admin\{Feature}\{Feature}Component - List/index componentsApp\Livewire\Admin\{Feature}\Options\{Action}Component - CRUD operations (Create, Edit)resources/views/livewire/admin/{feature}/{feature}.blade.phpMain/Frontend Components:
App\Livewire\Main\{Feature}\{Feature}Component - Public-facing componentsApp\Livewire\Main\Seller\{Feature}\{Feature}Componentresources/views/livewire/main/{feature}/{feature}.blade.phpPattern:
Componentproductlist.blade.php)Options subdirectoryAll UI uses custom Deep UI components in resources/views/components/deep/. These are Blade components, not Livewire.
Usage:
<x-deep.button variant="primary">Save</x-deep.button>
<x-deep.input wire:model="name" />
<x-deep.modal wire:model="showModal">Content</x-deep.modal>Why:
Icons:
<x-deep.icon name="user" />Icons use Heroicons but without the heroicon- prefix. The component handles the mapping.
App\Deep\Deep facade handles toast notifications:
use App\Deep\Deep;
Deep::toast(
text: __('admin.item_created'),
variant: 'success'
);Variants: success, danger, warning, info
Why not session flash:
All file uploads use Spatie Media Library. Never use Laravel's store() or storeAs().
Model Setup:
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class Gig extends Model implements HasMedia
{
use InteractsWithMedia;
public function registerMediaCollections(): void
{
$this->addMediaCollection('cover')->singleFile();
$this->addMediaCollection('gallery');
}
}Upload Pattern:
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
if ($file instanceof TemporaryUploadedFile) {
$extension = $file->getClientOriginalExtension();
$fileName = strtoupper(Str::random(32)) . '.' . $extension;
$model
->addMedia($file->getRealPath())
->usingFileName($fileName)
->withCustomProperties(['path_prefix' => 'collection-name/'])
->toMediaCollection('collection-name');
}Retrieval:
$model->getFirstMediaUrl('collection-name');
$model->getMedia('collection-name');Important: Always eager load media:
Gig::with('media')->get();Uses Spatie Permission. Permissions are checked in component mount() methods:
public function mount(): void
{
if (!auth()->user()->can('gigs.view_any')) {
abort(403);
}
}Permission Naming:
{resource}.view_any - List/index{resource}.view - Show single{resource}.create - Create{resource}.update - Update{resource}.delete - DeletePermissions are seeded in database/seeders/PermissionSeeder.php and translated in lang/en/permissions.php.
Never hardcode text. Everything uses translation keys:
__('admin.gigs_title')
__('messages.welcome')File Organization:
lang/en/admin.php - Admin panel onlylang/en/messages.php - Frontend/public pageslang/en/permissions.php - Permission namesWhy separate:
All components set SEO meta in render():
use Artesaos\SEOTools\Facades\SEOMeta;
public function render()
{
SEOMeta::setTitle(seo_title(__('admin.gigs_title')));
SEOMeta::setDescription(__('admin.gigs_subtitle'));
SEOMeta::setCanonical(route('admin.gigs.index'));
return view('livewire.admin.gigs.gigs', [
'gigs' => $this->gigs,
]);
}Admin titles use seo_title() helper:
Two main layouts:
components.layouts.admin - Admin panelcomponents.layouts.app - FrontendSet via attribute:
#[Layout('components.layouts.admin')]
class GigsComponent extends ComponentAlways eager load:
Gig::with(['category', 'user', 'media'])->get();Pagination path:
$gigs = Gig::paginate(15);
$gigs->setPath(route('admin.gigs.index'));Search pattern:
if ($this->search) {
$query->where('title', 'like', '%' . $this->search . '%');
}Filter pattern:
if ($this->statusFilter) {
$query->where('status', $this->statusFilter);
}Settings are stored in dedicated models:
SettingsGeneral - General settingsSettingsTheme - Theme settingsSettingsHome - Homepage settingsSettingsGig - Gig settingsSettingsMail - Mail settingsSettingsCurrency - Currency settingsPattern:
$settings = SettingsGeneral::first();
if ($settings) {
$this->logo_light = $settings->logo_light ?? '';
}Settings are singleton—one record per setting type.
Payment gateways are components in App\Livewire\Admin\Payments\Gateways\{Gateway}\{Gateway}Component:
StripeComponentPayPalComponentWalletComponentEach gateway has its own configuration form and processing logic.
Three-level category system:
Each level is a separate model with relationships:
Category -> hasMany(SubCategory)
SubCategory -> belongsTo(Category) -> hasMany(ChildCategory)
ChildCategory -> belongsTo(SubCategory)public function mount(): void
{
// Permission check
// Initial data loading
}
public function updatedSearch(): void
{
// Reset pagination on search
$this->resetPage();
}
public function clearFilters(): void
{
// Reset all filters
$this->reset(['search', 'statusFilter']);
$this->resetPage();
}
public function render()
{
// Build query
// Set SEO
// Return view
}public function save(): void
{
$validated = $this->validate([
'name' => ['required', 'string', 'max:255'],
]);
$model = Model::create($validated);
Deep::toast(
text: __('admin.item_created'),
variant: 'success'
);
// No redirect needed - toast handles feedback
}public function delete(int $id): void
{
$item = Model::findOrFail($id);
$item->delete();
Deep::toast(
text: __('admin.item_deleted'),
variant: 'success'
);
$this->dispatch('item-deleted'); // For Livewire updates
}Why Livewire over API:
Why Deep UI components:
Why Spatie packages:
Why translation keys everywhere:
Do:
Don't:
Don't Panic.
We've structured Deeplancer to be extensible. The codebase follows Laravel and Livewire conventions, so if you know the framework, you'll feel at home.
The architecture is modular—admin components, main components, services, and models are clearly separated. When extending, follow the existing patterns. Don't fight the framework.
We use Spatie packages for media and permissions. Deep UI components handle the frontend. Everything is namespaced and organized. If something feels off, there's probably a reason—check the existing implementations first.
Customize freely, but keep it maintainable. Use migrations for schema changes. Eager load relationships. Validate inputs. Check permissions. The usual stuff.
A friendly heads-up: We'll release updates, and when we do, you'll need to adapt your customizations. That's just how software works—things evolve. Extending helps, but it's not a magic shield. You can stay on your current version if it works for you, or roll up your sleeves when updates arrive. Either way, you're in control.
The answer is 42. The code is clean. Build something great.
— deep42