mirror of
https://github.com/openclassify/openclassify.git
synced 2026-04-14 11:12:09 -05:00
Fix module seeders PSR-4 namespaces
This commit is contained in:
parent
6ea371e372
commit
d2345cbeda
@ -1,26 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\IconColumn;
|
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Modules\Admin\Filament\Resources\CategoryResource\Pages;
|
use Modules\Admin\Filament\Resources\CategoryResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableColumns;
|
||||||
use Modules\Category\Models\Category;
|
use Modules\Category\Models\Category;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
class CategoryResource extends Resource
|
class CategoryResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = Category::class;
|
protected static ?string $model = Category::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-tag';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-tag';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'Catalog';
|
protected static string|UnitEnum|null $navigationGroup = 'Catalog';
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -30,7 +32,7 @@ class CategoryResource extends Resource
|
|||||||
TextInput::make('slug')->required()->maxLength(255)->unique(ignoreRecord: true),
|
TextInput::make('slug')->required()->maxLength(255)->unique(ignoreRecord: true),
|
||||||
TextInput::make('description')->maxLength(500),
|
TextInput::make('description')->maxLength(500),
|
||||||
TextInput::make('icon')->maxLength(100),
|
TextInput::make('icon')->maxLength(100),
|
||||||
Select::make('parent_id')->label('Parent Category')->options(fn () => Category::whereNull('parent_id')->pluck('name', 'id'))->nullable()->searchable(),
|
Select::make('parent_id')->label('Parent Category')->options(fn (): array => Category::rootIdNameOptions())->nullable()->searchable(),
|
||||||
TextInput::make('sort_order')->numeric()->default(0),
|
TextInput::make('sort_order')->numeric()->default(0),
|
||||||
Toggle::make('is_active')->default(true),
|
Toggle::make('is_active')->default(true),
|
||||||
]);
|
]);
|
||||||
@ -39,7 +41,7 @@ class CategoryResource extends Resource
|
|||||||
public static function table(Table $table): Table
|
public static function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table->columns([
|
return $table->columns([
|
||||||
TextColumn::make('id')->sortable(),
|
ResourceTableColumns::id(),
|
||||||
TextColumn::make('name')
|
TextColumn::make('name')
|
||||||
->searchable()
|
->searchable()
|
||||||
->formatStateUsing(fn (string $state, Category $record): string => $record->parent_id === null ? $state : '↳ '.$state)
|
->formatStateUsing(fn (string $state, Category $record): string => $record->parent_id === null ? $state : '↳ '.$state)
|
||||||
@ -47,7 +49,7 @@ class CategoryResource extends Resource
|
|||||||
TextColumn::make('parent.name')->label('Parent')->default('-'),
|
TextColumn::make('parent.name')->label('Parent')->default('-'),
|
||||||
TextColumn::make('children_count')->label('Subcategories'),
|
TextColumn::make('children_count')->label('Subcategories'),
|
||||||
TextColumn::make('listings_count')->label('Listings'),
|
TextColumn::make('listings_count')->label('Listings'),
|
||||||
IconColumn::make('is_active')->boolean(),
|
ResourceTableColumns::activeIcon(),
|
||||||
TextColumn::make('sort_order')->sortable(),
|
TextColumn::make('sort_order')->sortable(),
|
||||||
])->actions([
|
])->actions([
|
||||||
Action::make('toggleChildren')
|
Action::make('toggleChildren')
|
||||||
@ -55,11 +57,7 @@ class CategoryResource extends Resource
|
|||||||
->icon(fn (Category $record, Pages\ListCategories $livewire): string => $livewire->hasExpandedChildren($record) ? 'heroicon-o-chevron-down' : 'heroicon-o-chevron-right')
|
->icon(fn (Category $record, Pages\ListCategories $livewire): string => $livewire->hasExpandedChildren($record) ? 'heroicon-o-chevron-down' : 'heroicon-o-chevron-right')
|
||||||
->action(fn (Category $record, Pages\ListCategories $livewire) => $livewire->toggleChildren($record))
|
->action(fn (Category $record, Pages\ListCategories $livewire) => $livewire->toggleChildren($record))
|
||||||
->visible(fn (Category $record): bool => $record->parent_id === null && $record->children_count > 0),
|
->visible(fn (Category $record): bool => $record->parent_id === null && $record->children_count > 0),
|
||||||
EditAction::make(),
|
...ResourceTableActions::editActivityDelete(static::class),
|
||||||
Action::make('activities')
|
|
||||||
->icon('heroicon-o-clock')
|
|
||||||
->url(fn (Category $record): string => static::getUrl('activities', ['record' => $record])),
|
|
||||||
DeleteAction::make(),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,32 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\Action;
|
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\IconColumn;
|
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Filters\SelectFilter;
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
use Filament\Tables\Filters\TernaryFilter;
|
use Filament\Tables\Filters\TernaryFilter;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Modules\Admin\Filament\Resources\CityResource\Pages;
|
use Modules\Admin\Filament\Resources\CityResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableColumns;
|
||||||
use Modules\Location\Models\City;
|
use Modules\Location\Models\City;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
class CityResource extends Resource
|
class CityResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = City::class;
|
protected static ?string $model = City::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-building-office-2';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-building-office-2';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'Location';
|
protected static string|UnitEnum|null $navigationGroup = 'Location';
|
||||||
|
|
||||||
protected static ?string $label = 'City';
|
protected static ?string $label = 'City';
|
||||||
|
|
||||||
protected static ?string $pluralLabel = 'Cities';
|
protected static ?string $pluralLabel = 'Cities';
|
||||||
|
|
||||||
protected static ?int $navigationSort = 3;
|
protected static ?int $navigationSort = 3;
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -41,12 +45,12 @@ class CityResource extends Resource
|
|||||||
public static function table(Table $table): Table
|
public static function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table->columns([
|
return $table->columns([
|
||||||
TextColumn::make('id')->sortable(),
|
ResourceTableColumns::id(),
|
||||||
TextColumn::make('name')->searchable()->sortable(),
|
TextColumn::make('name')->searchable()->sortable(),
|
||||||
TextColumn::make('country.name')->label('Country')->searchable()->sortable(),
|
TextColumn::make('country.name')->label('Country')->searchable()->sortable(),
|
||||||
TextColumn::make('districts_count')->counts('districts')->label('Districts')->sortable(),
|
TextColumn::make('districts_count')->counts('districts')->label('Districts')->sortable(),
|
||||||
IconColumn::make('is_active')->boolean(),
|
ResourceTableColumns::activeIcon(),
|
||||||
TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true),
|
ResourceTableColumns::createdAtHidden(),
|
||||||
])->defaultSort('id', 'desc')->filters([
|
])->defaultSort('id', 'desc')->filters([
|
||||||
SelectFilter::make('country_id')
|
SelectFilter::make('country_id')
|
||||||
->label('Country')
|
->label('Country')
|
||||||
@ -61,13 +65,7 @@ class CityResource extends Resource
|
|||||||
blank: fn (Builder $query): Builder => $query,
|
blank: fn (Builder $query): Builder => $query,
|
||||||
),
|
),
|
||||||
TernaryFilter::make('is_active')->label('Active'),
|
TernaryFilter::make('is_active')->label('Active'),
|
||||||
])->actions([
|
])->actions(ResourceTableActions::editActivityDelete(static::class));
|
||||||
EditAction::make(),
|
|
||||||
Action::make('activities')
|
|
||||||
->icon('heroicon-o-clock')
|
|
||||||
->url(fn (City $record): string => static::getUrl('activities', ['record' => $record])),
|
|
||||||
DeleteAction::make(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\Action;
|
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\IconColumn;
|
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Filters\SelectFilter;
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
use Filament\Tables\Filters\TernaryFilter;
|
use Filament\Tables\Filters\TernaryFilter;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Modules\Admin\Filament\Resources\DistrictResource\Pages;
|
use Modules\Admin\Filament\Resources\DistrictResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableColumns;
|
||||||
use Modules\Location\Models\Country;
|
use Modules\Location\Models\Country;
|
||||||
use Modules\Location\Models\District;
|
use Modules\Location\Models\District;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
@ -24,10 +23,15 @@ use UnitEnum;
|
|||||||
class DistrictResource extends Resource
|
class DistrictResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = District::class;
|
protected static ?string $model = District::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-map';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-map';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'Location';
|
protected static string|UnitEnum|null $navigationGroup = 'Location';
|
||||||
|
|
||||||
protected static ?string $label = 'District';
|
protected static ?string $label = 'District';
|
||||||
|
|
||||||
protected static ?string $pluralLabel = 'Districts';
|
protected static ?string $pluralLabel = 'Districts';
|
||||||
|
|
||||||
protected static ?int $navigationSort = 4;
|
protected static ?int $navigationSort = 4;
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -42,16 +46,16 @@ class DistrictResource extends Resource
|
|||||||
public static function table(Table $table): Table
|
public static function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table->columns([
|
return $table->columns([
|
||||||
TextColumn::make('id')->sortable(),
|
ResourceTableColumns::id(),
|
||||||
TextColumn::make('name')->searchable()->sortable(),
|
TextColumn::make('name')->searchable()->sortable(),
|
||||||
TextColumn::make('city.name')->label('City')->searchable()->sortable(),
|
TextColumn::make('city.name')->label('City')->searchable()->sortable(),
|
||||||
TextColumn::make('city.country.name')->label('Country'),
|
TextColumn::make('city.country.name')->label('Country'),
|
||||||
IconColumn::make('is_active')->boolean(),
|
ResourceTableColumns::activeIcon(),
|
||||||
TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true),
|
ResourceTableColumns::createdAtHidden(),
|
||||||
])->defaultSort('id', 'desc')->filters([
|
])->defaultSort('id', 'desc')->filters([
|
||||||
SelectFilter::make('country_id')
|
SelectFilter::make('country_id')
|
||||||
->label('Country')
|
->label('Country')
|
||||||
->options(fn (): array => Country::query()->orderBy('name')->pluck('name', 'id')->all())
|
->options(fn (): array => Country::idNameOptions())
|
||||||
->query(fn (Builder $query, array $data): Builder => $query->when($data['value'] ?? null, fn (Builder $query, string $countryId): Builder => $query->whereHas('city', fn (Builder $cityQuery): Builder => $cityQuery->where('country_id', $countryId)))),
|
->query(fn (Builder $query, array $data): Builder => $query->when($data['value'] ?? null, fn (Builder $query, string $countryId): Builder => $query->whereHas('city', fn (Builder $cityQuery): Builder => $cityQuery->where('country_id', $countryId)))),
|
||||||
SelectFilter::make('city_id')
|
SelectFilter::make('city_id')
|
||||||
->label('City')
|
->label('City')
|
||||||
@ -59,13 +63,7 @@ class DistrictResource extends Resource
|
|||||||
->searchable()
|
->searchable()
|
||||||
->preload(),
|
->preload(),
|
||||||
TernaryFilter::make('is_active')->label('Active'),
|
TernaryFilter::make('is_active')->label('Active'),
|
||||||
])->actions([
|
])->actions(ResourceTableActions::editActivityDelete(static::class));
|
||||||
EditAction::make(),
|
|
||||||
Action::make('activities')
|
|
||||||
->icon('heroicon-o-clock')
|
|
||||||
->url(fn (District $record): string => static::getUrl('activities', ['record' => $record])),
|
|
||||||
DeleteAction::make(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
|
|||||||
@ -3,12 +3,10 @@
|
|||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TagsInput;
|
use Filament\Forms\Components\TagsInput;
|
||||||
use Filament\Forms\Components\TextInput;
|
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
@ -16,6 +14,7 @@ use Filament\Tables\Columns\IconColumn;
|
|||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Modules\Admin\Filament\Resources\ListingCustomFieldResource\Pages;
|
use Modules\Admin\Filament\Resources\ListingCustomFieldResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
use Modules\Category\Models\Category;
|
use Modules\Category\Models\Category;
|
||||||
use Modules\Listing\Models\ListingCustomField;
|
use Modules\Listing\Models\ListingCustomField;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
@ -23,8 +22,11 @@ use UnitEnum;
|
|||||||
class ListingCustomFieldResource extends Resource
|
class ListingCustomFieldResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = ListingCustomField::class;
|
protected static ?string $model = ListingCustomField::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-adjustments-horizontal';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-adjustments-horizontal';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'Catalog';
|
protected static string|UnitEnum|null $navigationGroup = 'Catalog';
|
||||||
|
|
||||||
protected static ?int $navigationSort = 30;
|
protected static ?int $navigationSort = 30;
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -63,11 +65,7 @@ class ListingCustomFieldResource extends Resource
|
|||||||
->live(),
|
->live(),
|
||||||
Select::make('category_id')
|
Select::make('category_id')
|
||||||
->label('Category')
|
->label('Category')
|
||||||
->options(fn (): array => Category::query()
|
->options(fn (): array => Category::activeIdNameOptions())
|
||||||
->where('is_active', true)
|
|
||||||
->orderBy('name')
|
|
||||||
->pluck('name', 'id')
|
|
||||||
->all())
|
|
||||||
->searchable()
|
->searchable()
|
||||||
->preload()
|
->preload()
|
||||||
->nullable()
|
->nullable()
|
||||||
@ -106,10 +104,7 @@ class ListingCustomFieldResource extends Resource
|
|||||||
TextColumn::make('sort_order')->sortable(),
|
TextColumn::make('sort_order')->sortable(),
|
||||||
])
|
])
|
||||||
->defaultSort('id', 'desc')
|
->defaultSort('id', 'desc')
|
||||||
->actions([
|
->actions(ResourceTableActions::editDelete());
|
||||||
EditAction::make(),
|
|
||||||
DeleteAction::make(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use A909M\FilamentStateFusion\Forms\Components\StateFusionSelect;
|
use A909M\FilamentStateFusion\Forms\Components\StateFusionSelect;
|
||||||
@ -7,9 +8,6 @@ use A909M\FilamentStateFusion\Tables\Filters\StateFusionSelectFilter;
|
|||||||
use App\Support\CountryCodeManager;
|
use App\Support\CountryCodeManager;
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Cheesegrits\FilamentGoogleMaps\Fields\Map;
|
use Cheesegrits\FilamentGoogleMaps\Fields\Map;
|
||||||
use Filament\Actions\Action;
|
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Forms\Components\DatePicker;
|
use Filament\Forms\Components\DatePicker;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
|
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
|
||||||
@ -30,6 +28,7 @@ use Filament\Tables\Filters\TernaryFilter;
|
|||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Modules\Admin\Filament\Resources\ListingResource\Pages;
|
use Modules\Admin\Filament\Resources\ListingResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
use Modules\Category\Models\Category;
|
use Modules\Category\Models\Category;
|
||||||
use Modules\Listing\Models\Listing;
|
use Modules\Listing\Models\Listing;
|
||||||
use Modules\Listing\Support\ListingCustomFieldSchemaBuilder;
|
use Modules\Listing\Support\ListingCustomFieldSchemaBuilder;
|
||||||
@ -43,7 +42,9 @@ use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput;
|
|||||||
class ListingResource extends Resource
|
class ListingResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = Listing::class;
|
protected static ?string $model = Listing::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-clipboard-document-list';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-clipboard-document-list';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'Catalog';
|
protected static string|UnitEnum|null $navigationGroup = 'Catalog';
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -61,7 +62,7 @@ class ListingResource extends Resource
|
|||||||
->required(),
|
->required(),
|
||||||
Select::make('category_id')
|
Select::make('category_id')
|
||||||
->label('Category')
|
->label('Category')
|
||||||
->options(fn () => Category::where('is_active', true)->pluck('name', 'id'))
|
->options(fn (): array => Category::activeIdNameOptions())
|
||||||
->searchable()
|
->searchable()
|
||||||
->live()
|
->live()
|
||||||
->afterStateUpdated(fn ($state, $set) => $set('custom_fields', []))
|
->afterStateUpdated(fn ($state, $set) => $set('custom_fields', []))
|
||||||
@ -83,10 +84,7 @@ class ListingResource extends Resource
|
|||||||
Toggle::make('is_featured')->default(false),
|
Toggle::make('is_featured')->default(false),
|
||||||
Select::make('country')
|
Select::make('country')
|
||||||
->label('Country')
|
->label('Country')
|
||||||
->options(fn (): array => Country::query()
|
->options(fn (): array => Country::nameOptions())
|
||||||
->orderBy('name')
|
|
||||||
->pluck('name', 'name')
|
|
||||||
->all())
|
|
||||||
->searchable()
|
->searchable()
|
||||||
->preload()
|
->preload()
|
||||||
->live()
|
->live()
|
||||||
@ -94,16 +92,7 @@ class ListingResource extends Resource
|
|||||||
->nullable(),
|
->nullable(),
|
||||||
Select::make('city')
|
Select::make('city')
|
||||||
->label('City')
|
->label('City')
|
||||||
->options(function (Get $get): array {
|
->options(fn (Get $get): array => City::nameOptions($get('country')))
|
||||||
$country = $get('country');
|
|
||||||
|
|
||||||
return City::query()
|
|
||||||
->where('is_active', true)
|
|
||||||
->when($country, fn (Builder $query, string $country): Builder => $query->whereHas('country', fn (Builder $countryQuery): Builder => $countryQuery->where('name', $country)))
|
|
||||||
->orderBy('name')
|
|
||||||
->pluck('name', 'name')
|
|
||||||
->all();
|
|
||||||
})
|
|
||||||
->searchable()
|
->searchable()
|
||||||
->preload()
|
->preload()
|
||||||
->nullable(),
|
->nullable(),
|
||||||
@ -161,16 +150,10 @@ class ListingResource extends Resource
|
|||||||
->searchable()
|
->searchable()
|
||||||
->preload(),
|
->preload(),
|
||||||
SelectFilter::make('country')
|
SelectFilter::make('country')
|
||||||
->options(fn (): array => Country::query()
|
->options(fn (): array => Country::nameOptions())
|
||||||
->orderBy('name')
|
|
||||||
->pluck('name', 'name')
|
|
||||||
->all())
|
|
||||||
->searchable(),
|
->searchable(),
|
||||||
SelectFilter::make('city')
|
SelectFilter::make('city')
|
||||||
->options(fn (): array => City::query()
|
->options(fn (): array => City::nameOptions(null, false))
|
||||||
->orderBy('name')
|
|
||||||
->pluck('name', 'name')
|
|
||||||
->all())
|
|
||||||
->searchable(),
|
->searchable(),
|
||||||
TernaryFilter::make('is_featured')->label('Featured'),
|
TernaryFilter::make('is_featured')->label('Featured'),
|
||||||
Filter::make('created_at')
|
Filter::make('created_at')
|
||||||
@ -197,13 +180,7 @@ class ListingResource extends Resource
|
|||||||
->filtersFormWidth('7xl')
|
->filtersFormWidth('7xl')
|
||||||
->persistFiltersInSession()
|
->persistFiltersInSession()
|
||||||
->defaultSort('id', 'desc')
|
->defaultSort('id', 'desc')
|
||||||
->actions([
|
->actions(ResourceTableActions::editActivityDelete(static::class));
|
||||||
EditAction::make(),
|
|
||||||
Action::make('activities')
|
|
||||||
->icon('heroicon-o-clock')
|
|
||||||
->url(fn (Listing $record): string => static::getUrl('activities', ['record' => $record])),
|
|
||||||
DeleteAction::make(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
|
|||||||
@ -1,31 +1,35 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\Action;
|
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Components\Toggle;
|
use Filament\Forms\Components\Toggle;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\IconColumn;
|
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Filters\SelectFilter;
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
use Filament\Tables\Filters\TernaryFilter;
|
use Filament\Tables\Filters\TernaryFilter;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Modules\Admin\Filament\Resources\LocationResource\Pages;
|
use Modules\Admin\Filament\Resources\LocationResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableColumns;
|
||||||
use Modules\Location\Models\Country;
|
use Modules\Location\Models\Country;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
class LocationResource extends Resource
|
class LocationResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = Country::class;
|
protected static ?string $model = Country::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-globe-alt';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-globe-alt';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'Location';
|
protected static string|UnitEnum|null $navigationGroup = 'Location';
|
||||||
|
|
||||||
protected static ?string $label = 'Country';
|
protected static ?string $label = 'Country';
|
||||||
|
|
||||||
protected static ?string $pluralLabel = 'Countries';
|
protected static ?string $pluralLabel = 'Countries';
|
||||||
|
|
||||||
protected static ?int $navigationSort = 2;
|
protected static ?int $navigationSort = 2;
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -41,17 +45,17 @@ class LocationResource extends Resource
|
|||||||
public static function table(Table $table): Table
|
public static function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table->columns([
|
return $table->columns([
|
||||||
TextColumn::make('id')->sortable(),
|
ResourceTableColumns::id(),
|
||||||
TextColumn::make('name')->searchable()->sortable(),
|
TextColumn::make('name')->searchable()->sortable(),
|
||||||
TextColumn::make('code')->searchable()->sortable(),
|
TextColumn::make('code')->searchable()->sortable(),
|
||||||
TextColumn::make('phone_code'),
|
TextColumn::make('phone_code'),
|
||||||
TextColumn::make('cities_count')->counts('cities')->label('Cities')->sortable(),
|
TextColumn::make('cities_count')->counts('cities')->label('Cities')->sortable(),
|
||||||
IconColumn::make('is_active')->boolean(),
|
ResourceTableColumns::activeIcon(),
|
||||||
TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true),
|
ResourceTableColumns::createdAtHidden(),
|
||||||
])->defaultSort('id', 'desc')->filters([
|
])->defaultSort('id', 'desc')->filters([
|
||||||
SelectFilter::make('code')
|
SelectFilter::make('code')
|
||||||
->label('Code')
|
->label('Code')
|
||||||
->options(fn (): array => Country::query()->orderBy('code')->pluck('code', 'code')->all()),
|
->options(fn (): array => Country::codeOptions()),
|
||||||
TernaryFilter::make('has_cities')
|
TernaryFilter::make('has_cities')
|
||||||
->label('Has cities')
|
->label('Has cities')
|
||||||
->queries(
|
->queries(
|
||||||
@ -60,13 +64,7 @@ class LocationResource extends Resource
|
|||||||
blank: fn (Builder $query): Builder => $query,
|
blank: fn (Builder $query): Builder => $query,
|
||||||
),
|
),
|
||||||
TernaryFilter::make('is_active')->label('Active'),
|
TernaryFilter::make('is_active')->label('Active'),
|
||||||
])->actions([
|
])->actions(ResourceTableActions::editActivityDelete(static::class));
|
||||||
EditAction::make(),
|
|
||||||
Action::make('activities')
|
|
||||||
->icon('heroicon-o-clock')
|
|
||||||
->url(fn (Country $record): string => static::getUrl('activities', ['record' => $record])),
|
|
||||||
DeleteAction::make(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Filament\Resources;
|
namespace Modules\Admin\Filament\Resources;
|
||||||
|
|
||||||
use A909M\FilamentStateFusion\Tables\Columns\StateFusionSelectColumn;
|
use A909M\FilamentStateFusion\Tables\Columns\StateFusionSelectColumn;
|
||||||
use A909M\FilamentStateFusion\Tables\Filters\StateFusionSelectFilter;
|
use A909M\FilamentStateFusion\Tables\Filters\StateFusionSelectFilter;
|
||||||
use Modules\User\App\Models\User;
|
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\Action;
|
|
||||||
use Filament\Actions\DeleteAction;
|
|
||||||
use Filament\Actions\EditAction;
|
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Tables\Columns\TextColumn;
|
use Filament\Tables\Columns\TextColumn;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Modules\Admin\Filament\Resources\UserResource\Pages;
|
use Modules\Admin\Filament\Resources\UserResource\Pages;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableActions;
|
||||||
|
use Modules\Admin\Support\Filament\ResourceTableColumns;
|
||||||
|
use Modules\User\App\Models\User;
|
||||||
use Modules\User\App\Support\Filament\UserFormFields;
|
use Modules\User\App\Support\Filament\UserFormFields;
|
||||||
use STS\FilamentImpersonate\Actions\Impersonate;
|
use STS\FilamentImpersonate\Actions\Impersonate;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
@ -20,7 +20,9 @@ use UnitEnum;
|
|||||||
class UserResource extends Resource
|
class UserResource extends Resource
|
||||||
{
|
{
|
||||||
protected static ?string $model = User::class;
|
protected static ?string $model = User::class;
|
||||||
|
|
||||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-users';
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-users';
|
||||||
|
|
||||||
protected static string|UnitEnum|null $navigationGroup = 'User Management';
|
protected static string|UnitEnum|null $navigationGroup = 'User Management';
|
||||||
|
|
||||||
public static function form(Schema $schema): Schema
|
public static function form(Schema $schema): Schema
|
||||||
@ -37,7 +39,7 @@ class UserResource extends Resource
|
|||||||
public static function table(Table $table): Table
|
public static function table(Table $table): Table
|
||||||
{
|
{
|
||||||
return $table->columns([
|
return $table->columns([
|
||||||
TextColumn::make('id')->sortable(),
|
ResourceTableColumns::id(),
|
||||||
TextColumn::make('name')->searchable()->sortable(),
|
TextColumn::make('name')->searchable()->sortable(),
|
||||||
TextColumn::make('email')->searchable()->sortable(),
|
TextColumn::make('email')->searchable()->sortable(),
|
||||||
TextColumn::make('roles.name')->badge()->label('Roles'),
|
TextColumn::make('roles.name')->badge()->label('Roles'),
|
||||||
@ -45,14 +47,9 @@ class UserResource extends Resource
|
|||||||
TextColumn::make('created_at')->dateTime()->sortable(),
|
TextColumn::make('created_at')->dateTime()->sortable(),
|
||||||
])->defaultSort('id', 'desc')->filters([
|
])->defaultSort('id', 'desc')->filters([
|
||||||
StateFusionSelectFilter::make('status'),
|
StateFusionSelectFilter::make('status'),
|
||||||
])->actions([
|
])->actions(ResourceTableActions::editActivityDelete(static::class, [
|
||||||
EditAction::make(),
|
|
||||||
Action::make('activities')
|
|
||||||
->icon('heroicon-o-clock')
|
|
||||||
->url(fn (User $record): string => static::getUrl('activities', ['record' => $record])),
|
|
||||||
Impersonate::make(),
|
Impersonate::make(),
|
||||||
DeleteAction::make(),
|
]));
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPages(): array
|
public static function getPages(): array
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Admin\Providers;
|
namespace Modules\Admin\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
@ -7,7 +8,7 @@ class AdminServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadMigrationsFrom(module_path('Admin', 'database/migrations'));
|
$this->loadMigrationsFrom(module_path('Admin', 'Database/migrations'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
|
|||||||
35
Modules/Admin/Support/Filament/ResourceTableActions.php
Normal file
35
Modules/Admin/Support/Filament/ResourceTableActions.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Admin\Support\Filament;
|
||||||
|
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
|
||||||
|
final class ResourceTableActions
|
||||||
|
{
|
||||||
|
public static function editDelete(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function editActivityDelete(string $resourceClass, array $afterActivity = []): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
EditAction::make(),
|
||||||
|
self::activities($resourceClass),
|
||||||
|
...$afterActivity,
|
||||||
|
DeleteAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function activities(string $resourceClass): Action
|
||||||
|
{
|
||||||
|
return Action::make('activities')
|
||||||
|
->icon('heroicon-o-clock')
|
||||||
|
->url(fn ($record): string => $resourceClass::getUrl('activities', ['record' => $record]));
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Modules/Admin/Support/Filament/ResourceTableColumns.php
Normal file
27
Modules/Admin/Support/Filament/ResourceTableColumns.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Admin\Support\Filament;
|
||||||
|
|
||||||
|
use Filament\Tables\Columns\IconColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
|
||||||
|
final class ResourceTableColumns
|
||||||
|
{
|
||||||
|
public static function id(string $name = 'id'): TextColumn
|
||||||
|
{
|
||||||
|
return TextColumn::make($name)->sortable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function activeIcon(string $name = 'is_active', string $label = 'Active'): IconColumn
|
||||||
|
{
|
||||||
|
return IconColumn::make($name)->label($label)->boolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createdAtHidden(string $name = 'created_at'): TextColumn
|
||||||
|
{
|
||||||
|
return TextColumn::make($name)
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Category\Models;
|
namespace Modules\Category\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
@ -32,6 +33,7 @@ class Category extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $fillable = ['name', 'slug', 'description', 'icon', 'parent_id', 'level', 'sort_order', 'is_active'];
|
protected $fillable = ['name', 'slug', 'description', 'icon', 'parent_id', 'level', 'sort_order', 'is_active'];
|
||||||
|
|
||||||
protected $casts = ['is_active' => 'boolean'];
|
protected $casts = ['is_active' => 'boolean'];
|
||||||
|
|
||||||
public function getActivitylogOptions(): LogOptions
|
public function getActivitylogOptions(): LogOptions
|
||||||
@ -103,6 +105,25 @@ class Category extends Model
|
|||||||
->get(['id', 'name']);
|
->get(['id', 'name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function activeIdNameOptions(): array
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->active()
|
||||||
|
->ordered()
|
||||||
|
->pluck('name', 'id')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function rootIdNameOptions(): array
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->active()
|
||||||
|
->whereNull('parent_id')
|
||||||
|
->ordered()
|
||||||
|
->pluck('name', 'id')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
public static function themePills(int $limit = 8): Collection
|
public static function themePills(int $limit = 8): Collection
|
||||||
{
|
{
|
||||||
return static::query()
|
return static::query()
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Category\Providers;
|
namespace Modules\Category\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
@ -9,7 +10,7 @@ class CategoryServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
|
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path($this->moduleName, 'routes/web.php'));
|
$this->loadRoutesFrom(module_path($this->moduleName, 'routes/web.php'));
|
||||||
$this->loadViewsFrom(module_path($this->moduleName, 'resources/views'), 'category');
|
$this->loadViewsFrom(module_path($this->moduleName, 'resources/views'), 'category');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ class ConversationServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadMigrationsFrom(module_path('Conversation', 'database/migrations'));
|
$this->loadMigrationsFrom(module_path('Conversation', 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path('Conversation', 'routes/web.php'));
|
$this->loadRoutesFrom(module_path('Conversation', 'routes/web.php'));
|
||||||
$this->loadViewsFrom(module_path('Conversation', 'resources/views'), 'conversation');
|
$this->loadViewsFrom(module_path('Conversation', 'resources/views'), 'conversation');
|
||||||
|
|
||||||
@ -18,7 +18,5 @@ class ConversationServiceProvider extends ServiceProvider
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void {}
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class DemoServiceProvider extends ServiceProvider
|
|||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->guardConfiguration();
|
$this->guardConfiguration();
|
||||||
$this->loadMigrationsFrom(module_path('Demo', 'database/migrations'));
|
$this->loadMigrationsFrom(module_path('Demo', 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path('Demo', 'routes/web.php'));
|
$this->loadRoutesFrom(module_path('Demo', 'routes/web.php'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,10 @@ class FavoriteServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadMigrationsFrom(module_path('Favorite', 'database/migrations'));
|
$this->loadMigrationsFrom(module_path('Favorite', 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path('Favorite', 'routes/web.php'));
|
$this->loadRoutesFrom(module_path('Favorite', 'routes/web.php'));
|
||||||
$this->loadViewsFrom(module_path('Favorite', 'resources/views'), 'favorite');
|
$this->loadViewsFrom(module_path('Favorite', 'resources/views'), 'favorite');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void {}
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Listing\Providers;
|
namespace Modules\Listing\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
@ -6,12 +7,13 @@ use Illuminate\Support\ServiceProvider;
|
|||||||
class ListingServiceProvider extends ServiceProvider
|
class ListingServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
protected string $moduleName = 'Listing';
|
protected string $moduleName = 'Listing';
|
||||||
|
|
||||||
protected string $moduleNameLower = 'listing';
|
protected string $moduleNameLower = 'listing';
|
||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadViewsFrom(module_path($this->moduleName, 'resources/views'), $this->moduleNameLower);
|
$this->loadViewsFrom(module_path($this->moduleName, 'resources/views'), $this->moduleNameLower);
|
||||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
|
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path($this->moduleName, 'routes/web.php'));
|
$this->loadRoutesFrom(module_path($this->moduleName, 'routes/web.php'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Location\Models;
|
namespace Modules\Location\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Spatie\Activitylog\LogOptions;
|
use Spatie\Activitylog\LogOptions;
|
||||||
use Spatie\Activitylog\Traits\LogsActivity;
|
use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
@ -10,8 +12,14 @@ class City extends Model
|
|||||||
use LogsActivity;
|
use LogsActivity;
|
||||||
|
|
||||||
protected $fillable = ['name', 'country_id', 'is_active'];
|
protected $fillable = ['name', 'country_id', 'is_active'];
|
||||||
|
|
||||||
protected $casts = ['is_active' => 'boolean'];
|
protected $casts = ['is_active' => 'boolean'];
|
||||||
|
|
||||||
|
public function scopeActive(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->where('is_active', true);
|
||||||
|
}
|
||||||
|
|
||||||
public function getActivitylogOptions(): LogOptions
|
public function getActivitylogOptions(): LogOptions
|
||||||
{
|
{
|
||||||
return LogOptions::defaults()
|
return LogOptions::defaults()
|
||||||
@ -20,6 +28,29 @@ class City extends Model
|
|||||||
->dontSubmitEmptyLogs();
|
->dontSubmitEmptyLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function country() { return $this->belongsTo(Country::class); }
|
public function country()
|
||||||
public function districts() { return $this->hasMany(District::class); }
|
{
|
||||||
|
return $this->belongsTo(Country::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function districts()
|
||||||
|
{
|
||||||
|
return $this->hasMany(District::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function nameOptions(?string $countryName = null, bool $onlyActive = true): array
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->when($onlyActive, fn (Builder $query): Builder => $query->active())
|
||||||
|
->when(
|
||||||
|
$countryName && trim($countryName) !== '',
|
||||||
|
fn (Builder $query): Builder => $query->whereHas(
|
||||||
|
'country',
|
||||||
|
fn (Builder $countryQuery): Builder => $countryQuery->where('name', trim($countryName)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
->orderBy('name')
|
||||||
|
->pluck('name', 'name')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Location\Models;
|
namespace Modules\Location\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Spatie\Activitylog\LogOptions;
|
use Spatie\Activitylog\LogOptions;
|
||||||
use Spatie\Activitylog\Traits\LogsActivity;
|
use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
@ -10,8 +12,14 @@ class Country extends Model
|
|||||||
use LogsActivity;
|
use LogsActivity;
|
||||||
|
|
||||||
protected $fillable = ['name', 'code', 'phone_code', 'flag', 'is_active'];
|
protected $fillable = ['name', 'code', 'phone_code', 'flag', 'is_active'];
|
||||||
|
|
||||||
protected $casts = ['is_active' => 'boolean'];
|
protected $casts = ['is_active' => 'boolean'];
|
||||||
|
|
||||||
|
public function scopeActive(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->where('is_active', true);
|
||||||
|
}
|
||||||
|
|
||||||
public function getActivitylogOptions(): LogOptions
|
public function getActivitylogOptions(): LogOptions
|
||||||
{
|
{
|
||||||
return LogOptions::defaults()
|
return LogOptions::defaults()
|
||||||
@ -24,4 +32,31 @@ class Country extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(City::class);
|
return $this->hasMany(City::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function idNameOptions(bool $onlyActive = false): array
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->when($onlyActive, fn (Builder $query): Builder => $query->active())
|
||||||
|
->orderBy('name')
|
||||||
|
->pluck('name', 'id')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function codeOptions(bool $onlyActive = false): array
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->when($onlyActive, fn (Builder $query): Builder => $query->active())
|
||||||
|
->orderBy('code')
|
||||||
|
->pluck('code', 'code')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function nameOptions(bool $onlyActive = false): array
|
||||||
|
{
|
||||||
|
return static::query()
|
||||||
|
->when($onlyActive, fn (Builder $query): Builder => $query->active())
|
||||||
|
->orderBy('name')
|
||||||
|
->pluck('name', 'name')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Modules\Location\Providers;
|
namespace Modules\Location\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
@ -9,7 +10,7 @@ class LocationServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
|
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path($this->moduleName, 'routes/web.php'));
|
$this->loadRoutesFrom(module_path($this->moduleName, 'routes/web.php'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,10 @@ class UserServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadMigrationsFrom(module_path('User', 'database/migrations'));
|
$this->loadMigrationsFrom(module_path('User', 'Database/migrations'));
|
||||||
$this->loadRoutesFrom(module_path('User', 'routes/web.php'));
|
$this->loadRoutesFrom(module_path('User', 'routes/web.php'));
|
||||||
$this->loadViewsFrom(module_path('User', 'resources/views'), 'user');
|
$this->loadViewsFrom(module_path('User', 'resources/views'), 'user');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void {}
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ class VideoServiceProvider extends ServiceProvider
|
|||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
$this->loadViewsFrom(module_path('Video', 'resources/views'), 'video');
|
$this->loadViewsFrom(module_path('Video', 'resources/views'), 'video');
|
||||||
$this->loadMigrationsFrom(module_path('Video', 'database/migrations'));
|
$this->loadMigrationsFrom(module_path('Video', 'Database/migrations'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
|
|||||||
300
README.md
300
README.md
@ -1,128 +1,76 @@
|
|||||||
# OpenClassify
|
# OpenClassify
|
||||||
|
|
||||||
A modern classified ads platform built with Laravel 12, FilamentPHP v5, and Laravel Modules — similar to Letgo and Sahibinden.
|
OpenClassify is a modular classifieds marketplace built with Laravel 12 and Filament v5.
|
||||||
|
|
||||||
## Features
|
## Core Stack
|
||||||
|
|
||||||
- 🛍️ **Classified Listings** — Browse, search, and post ads across categories
|
- Laravel 12
|
||||||
- 🗂️ **Categories** — Hierarchical categories with icons
|
- FilamentPHP v5
|
||||||
- 📍 **Locations** — Country and city management
|
- `nwidart/laravel-modules`
|
||||||
- 👤 **User Profiles** — Manage your listings and account
|
- Blade + Tailwind + Vite
|
||||||
- 🔐 **Admin Panel** — Full control via FilamentPHP v5 at `/admin`
|
- Spatie Permission
|
||||||
- 🧭 **Frontend Panel** — Authenticated users manage listings, profile, videos, favorites, and inbox at `/panel`
|
- Laravel Reverb + Echo (realtime chat)
|
||||||
- 🧪 **Demo Mode** — Per-visitor PostgreSQL schema provisioning with seeded data and automatic cleanup
|
|
||||||
- 🌍 **10 Languages** — English, Turkish, Arabic, German, French, Spanish, Portuguese, Russian, Chinese, Japanese
|
|
||||||
- 🐳 **Docker Ready** — One-command production and development setup
|
|
||||||
- ☁️ **GitHub Codespaces** — Zero-config cloud development
|
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
## Tech Stack
|
All business features live in `Modules/*` (routes, services, models, resources, views, seeders).
|
||||||
|
|
||||||
| Layer | Technology |
|
Create a new module:
|
||||||
|-------|-----------|
|
|
||||||
| Framework | Laravel 12 |
|
|
||||||
| Admin UI | FilamentPHP v5 |
|
|
||||||
| Modules | nWidart/laravel-modules v11 |
|
|
||||||
| Auth/Roles | Spatie Laravel Permission |
|
|
||||||
| Frontend | Blade + TailwindCSS + Vite |
|
|
||||||
| Database | PostgreSQL (required for demo mode) |
|
|
||||||
| Cache/Queue | Database or Redis |
|
|
||||||
|
|
||||||
## Quick Start (Docker)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
php artisan module:make ModuleName
|
||||||
git clone https://github.com/openclassify/openclassify.git
|
```
|
||||||
cd openclassify
|
|
||||||
|
|
||||||
# Copy environment file
|
Enable it in `modules_statuses.json`.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
|
|
||||||
# Start with Docker Compose (production-like)
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
# The application will be available at http://localhost:8000
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Default Accounts
|
App URLs:
|
||||||
|
|
||||||
| Role | Email | Password |
|
- Frontend: `http://localhost:8000`
|
||||||
|------|-------|----------|
|
- Admin: `http://localhost:8000/admin`
|
||||||
| Admin | a@a.com | 236330 |
|
- Panel: `http://localhost:8000/panel`
|
||||||
| Member | b@b.com | 36330 |
|
|
||||||
|
|
||||||
These accounts are seeded by `Modules\User\Database\Seeders\AuthUserSeeder`. In demo mode, demo preparation still auto-logs the visitor into the schema-local admin account.
|
### Local
|
||||||
|
|
||||||
**Admin Panel:** http://localhost:8000/admin
|
Requirements: PHP 8.2+, Composer, Node 18+, database server.
|
||||||
**Frontend Panel:** http://localhost:8000/panel
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Development Setup
|
|
||||||
|
|
||||||
### Option 1: GitHub Codespaces (Zero Config)
|
|
||||||
|
|
||||||
1. Click **Code → Codespaces → New codespace** on GitHub
|
|
||||||
2. Wait for the environment to build (~2 minutes)
|
|
||||||
3. The app starts automatically at port 8000
|
|
||||||
|
|
||||||
### Option 2: Docker Development
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start development environment with hot reload
|
|
||||||
docker compose -f docker-compose.dev.yml up -d
|
|
||||||
|
|
||||||
# View logs
|
|
||||||
docker compose -f docker-compose.dev.yml logs -f app
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 3: Local (PHP + Node)
|
|
||||||
|
|
||||||
**Requirements:** PHP 8.2+, Composer, Node 18+, PostgreSQL for demo mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies
|
|
||||||
composer install
|
composer install
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# Setup environment
|
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
php artisan key:generate
|
php artisan key:generate
|
||||||
|
|
||||||
# Database (SQLite for quick start)
|
|
||||||
touch database/database.sqlite
|
|
||||||
php artisan migrate
|
php artisan migrate
|
||||||
php artisan db:seed
|
php artisan db:seed
|
||||||
|
|
||||||
# Start all services (server + queue + vite)
|
|
||||||
composer run dev
|
composer run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Seeded Accounts
|
||||||
|
|
||||||
|
| Role | Email | Password |
|
||||||
|
|------|-------|----------|
|
||||||
|
| Admin | `a@a.com` | `236330` |
|
||||||
|
| Member | `b@b.com` | `36330` |
|
||||||
|
|
||||||
## Demo Mode
|
## Demo Mode
|
||||||
|
|
||||||
Demo mode is designed for isolated visitor sessions. When enabled, each visitor can provision a private temporary marketplace backed by its own PostgreSQL schema.
|
Demo mode provisions a temporary, per-visitor marketplace schema.
|
||||||
|
|
||||||
### Requirements
|
Requirements:
|
||||||
|
|
||||||
- `DB_CONNECTION=pgsql`
|
- `DB_CONNECTION=pgsql`
|
||||||
- `DEMO=1`
|
- `DEMO=1`
|
||||||
- database-backed session / cache / queue drivers are supported and will stay on the public schema via `pgsql_public`
|
|
||||||
|
|
||||||
If `DEMO=1` is set while the app is not using PostgreSQL, the application fails fast during boot.
|
Minimal `.env`:
|
||||||
|
|
||||||
### Runtime Behavior
|
|
||||||
|
|
||||||
- On the first guest homepage visit, the primary visible CTA is a single large `Prepare Demo` button.
|
|
||||||
- The homepage shows how long the temporary demo will live before automatic deletion.
|
|
||||||
- Clicking `Prepare Demo` provisions a visitor-specific schema, runs `migrate` and `db:seed`, and logs the visitor into the seeded admin account.
|
|
||||||
- The same browser reuses its active demo instead of creating duplicate schemas.
|
|
||||||
- Demo lifetime defaults to `360` minutes from explicit prepare / reopen time.
|
|
||||||
- Expired demos are removed by `demo:cleanup`, which is scheduled hourly.
|
|
||||||
|
|
||||||
### Environment
|
|
||||||
|
|
||||||
```env
|
```env
|
||||||
DB_CONNECTION=pgsql
|
|
||||||
DEMO=1
|
DEMO=1
|
||||||
DEMO_TTL_MINUTES=360
|
DEMO_TTL_MINUTES=360
|
||||||
DEMO_SCHEMA_PREFIX=demo_
|
DEMO_SCHEMA_PREFIX=demo_
|
||||||
@ -131,195 +79,71 @@ DEMO_LOGIN_EMAIL=a@a.com
|
|||||||
DEMO_PUBLIC_SCHEMA=public
|
DEMO_PUBLIC_SCHEMA=public
|
||||||
```
|
```
|
||||||
|
|
||||||
### Commands
|
Commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php artisan migrate --force
|
|
||||||
php artisan db:seed --force
|
|
||||||
php artisan demo:prepare
|
php artisan demo:prepare
|
||||||
php artisan demo:cleanup
|
php artisan demo:cleanup
|
||||||
```
|
```
|
||||||
|
|
||||||
### Panels
|
Notes:
|
||||||
|
|
||||||
| Panel | URL | Access |
|
- First guest homepage shows only `Prepare Demo`.
|
||||||
|-------|-----|--------|
|
- `Prepare Demo` creates/reuses a private schema and logs in seeded admin.
|
||||||
| Admin | `/admin` | Users with `admin` role |
|
- Expired demos are cleaned up automatically (hourly schedule).
|
||||||
| Frontend Panel | `/panel` | All authenticated users |
|
|
||||||
|
|
||||||
### Roles (Spatie Permission)
|
## Realtime Chat (Reverb)
|
||||||
|
|
||||||
| Role | Access |
|
Set `.env`:
|
||||||
|------|--------|
|
|
||||||
| `admin` | Full admin panel access |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Realtime Chat (Laravel Reverb)
|
|
||||||
|
|
||||||
This project already uses Laravel Reverb + Echo for inbox and listing chat realtime updates.
|
|
||||||
|
|
||||||
### 1. Environment
|
|
||||||
|
|
||||||
Set these values in `.env`:
|
|
||||||
|
|
||||||
```env
|
```env
|
||||||
BROADCAST_CONNECTION=reverb
|
BROADCAST_CONNECTION=reverb
|
||||||
|
REVERB_APP_ID=app_id
|
||||||
REVERB_APP_ID=480227
|
REVERB_APP_KEY=app_key
|
||||||
REVERB_APP_KEY=your_key
|
REVERB_APP_SECRET=app_secret
|
||||||
REVERB_APP_SECRET=your_secret
|
|
||||||
REVERB_HOST=localhost
|
REVERB_HOST=localhost
|
||||||
REVERB_PORT=8080
|
REVERB_PORT=8080
|
||||||
REVERB_SCHEME=http
|
REVERB_SCHEME=http
|
||||||
REVERB_SERVER_HOST=0.0.0.0
|
REVERB_SERVER_HOST=0.0.0.0
|
||||||
REVERB_SERVER_PORT=8080
|
REVERB_SERVER_PORT=8080
|
||||||
|
|
||||||
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
||||||
VITE_REVERB_HOST="${REVERB_HOST}"
|
VITE_REVERB_HOST="${REVERB_HOST}"
|
||||||
VITE_REVERB_PORT="${REVERB_PORT}"
|
VITE_REVERB_PORT="${REVERB_PORT}"
|
||||||
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Start Services
|
Start:
|
||||||
|
|
||||||
Use one command:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
composer run dev
|
composer run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Or run separately:
|
Channel strategy:
|
||||||
|
|
||||||
```bash
|
- private channel: `users.{id}.inbox`
|
||||||
php artisan serve
|
- events: `InboxMessageCreated`, `ConversationReadUpdated`
|
||||||
php artisan reverb:start --host=0.0.0.0 --port=8080
|
|
||||||
php artisan queue:listen --tries=1 --timeout=0
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. How It Works in This Codebase
|
## Test and Build
|
||||||
|
|
||||||
- Private channel: `users.{id}.inbox`
|
|
||||||
- Channel authorization: `Modules/Conversation/App/Providers/ConversationServiceProvider.php`
|
|
||||||
- Broadcast events:
|
|
||||||
- `InboxMessageCreated` (`.inbox.message.created`)
|
|
||||||
- `ConversationReadUpdated` (`.inbox.read.updated`)
|
|
||||||
- Frontend subscriptions: `Modules/Conversation/resources/assets/js/conversation.js`
|
|
||||||
- Echo bootstrap: `resources/js/echo.js`
|
|
||||||
|
|
||||||
### 4. Quick Verification
|
|
||||||
|
|
||||||
1. Open two different authenticated sessions (for example `a@a.com` and `b@b.com`).
|
|
||||||
2. Go to `/panel/inbox` in both sessions.
|
|
||||||
3. Send a message from one session.
|
|
||||||
4. Confirm in the other session:
|
|
||||||
- thread updates instantly,
|
|
||||||
- inbox ordering/unread state updates,
|
|
||||||
- header inbox badge updates.
|
|
||||||
|
|
||||||
### 5. Troubleshooting
|
|
||||||
|
|
||||||
- No realtime updates:
|
|
||||||
- check `php artisan reverb:start` is running,
|
|
||||||
- check Vite is running (`npm run dev`) and assets are rebuilt.
|
|
||||||
- Private channel auth fails (`403`):
|
|
||||||
- verify user is authenticated in the same browser/session.
|
|
||||||
- WebSocket connection fails:
|
|
||||||
- verify `REVERB_HOST/PORT/SCHEME` and matching `VITE_REVERB_*` values,
|
|
||||||
- run `php artisan optimize:clear` after env changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Contributors
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://openclassify.com">
|
|
||||||
<img src="https://raw.githubusercontent.com/openclassify/openclassify/master/public/openclassify-logo.png" width="220" alt="OpenClassify Logo">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
OpenClassify is a modular open source classified platform built with Laravel.
|
|
||||||
|
|
||||||
- Website: [openclassify.com](https://openclassify.com)
|
|
||||||
- Package: [openclassify/openclassify](https://packagist.org/packages/openclassify/openclassify)
|
|
||||||
|
|
||||||
This project is maintained and improved by its contributors.
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://github.com/openclassify/openclassify/graphs/contributors">
|
|
||||||
<img src="https://contrib.rocks/image?repo=openclassify/openclassify" alt="OpenClassify Contributors">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## Creating a New Module
|
|
||||||
|
|
||||||
```bash
|
|
||||||
php artisan module:make ModuleName
|
|
||||||
```
|
|
||||||
|
|
||||||
Then add to `modules_statuses.json`:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ModuleName": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Adding a Filament Resource to Admin Panel
|
|
||||||
|
|
||||||
Resources are auto-discovered from `Modules/Admin/Filament/Resources/`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Language Support
|
|
||||||
|
|
||||||
Languages are in `lang/{locale}/messages.php`. To add a new language:
|
|
||||||
|
|
||||||
1. Create `lang/{locale}/messages.php`
|
|
||||||
2. Switch language via: `GET /lang/{locale}`
|
|
||||||
|
|
||||||
Supported locales: `en`, `tr`, `ar`, `de`, `fr`, `es`, `pt`, `ru`, `zh`, `ja`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php artisan test
|
php artisan test
|
||||||
|
php artisan optimize:clear
|
||||||
|
php artisan view:cache
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## Production Checklist
|
||||||
|
|
||||||
## Production Deployment
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
```env
|
|
||||||
APP_ENV=production
|
|
||||||
APP_DEBUG=false
|
|
||||||
APP_URL=https://yourdomain.com
|
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
|
||||||
DB_HOST=your-db-host
|
|
||||||
DB_DATABASE=openclassify
|
|
||||||
DB_USERNAME=openclassify
|
|
||||||
DB_PASSWORD=your-secure-password
|
|
||||||
|
|
||||||
REDIS_HOST=your-redis-host
|
|
||||||
CACHE_STORE=redis
|
|
||||||
SESSION_DRIVER=redis
|
|
||||||
QUEUE_CONNECTION=redis
|
|
||||||
```
|
|
||||||
|
|
||||||
### Post-Deploy Commands
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php artisan migrate --force
|
php artisan migrate --force
|
||||||
php artisan db:seed --force # Only on first deploy
|
php artisan db:seed --force
|
||||||
|
php artisan storage:link
|
||||||
php artisan config:cache
|
php artisan config:cache
|
||||||
php artisan route:cache
|
php artisan route:cache
|
||||||
php artisan view:cache
|
php artisan view:cache
|
||||||
php artisan storage:link
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
- Website: [openclassify.com](https://openclassify.com)
|
||||||
|
- Package: [openclassify/openclassify](https://packagist.org/packages/openclassify/openclassify)
|
||||||
|
- Contributors: [GitHub graph](https://github.com/openclassify/openclassify/graphs/contributors)
|
||||||
|
|||||||
@ -146,10 +146,10 @@ return [
|
|||||||
// config/
|
// config/
|
||||||
'config' => ['path' => 'config', 'generate' => true],
|
'config' => ['path' => 'config', 'generate' => true],
|
||||||
|
|
||||||
// database/
|
// Database/
|
||||||
'factory' => ['path' => 'database/factories', 'generate' => true],
|
'factory' => ['path' => 'Database/Factories', 'generate' => true],
|
||||||
'migration' => ['path' => 'database/migrations', 'generate' => true],
|
'migration' => ['path' => 'Database/migrations', 'generate' => true],
|
||||||
'seeder' => ['path' => 'database/seeders', 'generate' => true],
|
'seeder' => ['path' => 'Database/Seeders', 'generate' => true],
|
||||||
|
|
||||||
// lang/
|
// lang/
|
||||||
'lang' => ['path' => 'lang', 'generate' => false],
|
'lang' => ['path' => 'lang', 'generate' => false],
|
||||||
|
|||||||
@ -6,25 +6,20 @@ use Illuminate\Support\Facades\Schema;
|
|||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
public function up()
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('breezy_sessions', function (Blueprint $table) {
|
Schema::create('breezy_sessions', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->morphs('authenticatable');
|
$table->morphs('authenticatable');
|
||||||
$table->string('panel_id')->nullable();
|
$table->string('panel_id')->nullable();
|
||||||
$table->string('guard')->nullable();
|
|
||||||
$table->string('ip_address', 45)->nullable();
|
|
||||||
$table->text('user_agent')->nullable();
|
|
||||||
$table->timestamp('expires_at')->nullable();
|
|
||||||
$table->text('two_factor_secret')->nullable();
|
$table->text('two_factor_secret')->nullable();
|
||||||
$table->text('two_factor_recovery_codes')->nullable();
|
$table->text('two_factor_recovery_codes')->nullable();
|
||||||
$table->timestamp('two_factor_confirmed_at')->nullable();
|
$table->timestamp('two_factor_confirmed_at')->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down()
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::dropIfExists('breezy_sessions');
|
Schema::dropIfExists('breezy_sessions');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
<?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('breezy_sessions', function (Blueprint $table) {
|
|
||||||
$table->dropColumn([
|
|
||||||
'guard',
|
|
||||||
'ip_address',
|
|
||||||
'user_agent',
|
|
||||||
'expires_at',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
Schema::table('breezy_sessions', function (Blueprint $table) {
|
|
||||||
$table->after('panel_id', function (BluePrint $table) {
|
|
||||||
$table->string('guard')->nullable();
|
|
||||||
$table->string('ip_address', 45)->nullable();
|
|
||||||
$table->text('user_agent')->nullable();
|
|
||||||
$table->timestamp('expires_at')->nullable();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,27 +1,31 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class CreateActivityLogTable extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
public function up()
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::connection(config('activitylog.database_connection'))->create(config('activitylog.table_name'), function (Blueprint $table) {
|
Schema::connection(config('activitylog.database_connection'))
|
||||||
|
->create(config('activitylog.table_name'), function (Blueprint $table): void {
|
||||||
$table->bigIncrements('id');
|
$table->bigIncrements('id');
|
||||||
$table->string('log_name')->nullable();
|
$table->string('log_name')->nullable();
|
||||||
$table->text('description');
|
$table->text('description');
|
||||||
$table->nullableMorphs('subject', 'subject');
|
$table->nullableMorphs('subject', 'subject');
|
||||||
|
$table->string('event')->nullable();
|
||||||
$table->nullableMorphs('causer', 'causer');
|
$table->nullableMorphs('causer', 'causer');
|
||||||
$table->json('properties')->nullable();
|
$table->json('properties')->nullable();
|
||||||
|
$table->uuid('batch_uuid')->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
$table->index('log_name');
|
$table->index('log_name');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down()
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name'));
|
Schema::connection(config('activitylog.database_connection'))
|
||||||
}
|
->dropIfExists(config('activitylog.table_name'));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
|
|
||||||
class AddEventColumnToActivityLogTable extends Migration
|
|
||||||
{
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
|
||||||
$table->string('event')->nullable()->after('subject_type');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
|
||||||
$table->dropColumn('event');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
|
|
||||||
class AddBatchUuidColumnToActivityLogTable extends Migration
|
|
||||||
{
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
|
||||||
$table->uuid('batch_uuid')->nullable()->after('properties');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
|
||||||
$table->dropColumn('batch_uuid');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user