Remove policy restrictions for guys

This commit is contained in:
fatihalp 2026-03-03 18:40:42 +03:00
parent 3428bd3e43
commit cf313e750f
21 changed files with 475 additions and 1061 deletions

View File

@ -10,22 +10,29 @@ 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\Select;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\SpatieMediaLibraryImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Modules\Admin\Filament\Resources\ListingResource\Pages;
use Modules\Category\Models\Category;
use Modules\Listing\Models\Listing;
use Modules\Listing\Support\ListingPanelHelper;
use Tapp\FilamentCountryCodeField\Forms\Components\CountryCodeSelect;
use Modules\Location\Models\City;
use Modules\Location\Models\Country;
use UnitEnum;
use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput;
@ -49,16 +56,37 @@ class ListingResource extends Resource
->default(fn () => ListingPanelHelper::defaultCurrency())
->required(),
Select::make('category_id')->label('Category')->options(fn () => Category::where('is_active', true)->pluck('name', 'id'))->searchable()->nullable(),
Select::make('user_id')->relationship('user', 'email')->label('Owner')->searchable()->preload()->nullable(),
StateFusionSelect::make('status')->required(),
PhoneInput::make('contact_phone')->defaultCountry(CountryCodeManager::defaultCountryIso2())->nullable(),
TextInput::make('contact_email')->email()->maxLength(255),
Toggle::make('is_featured')->default(false),
TextInput::make('city')->maxLength(100),
CountryCodeSelect::make('country')
Select::make('country')
->label('Country')
->default(fn () => CountryCodeManager::defaultCountryCode())
->formatStateUsing(fn ($state): ?string => CountryCodeManager::countryCodeFromLabelOrCode($state))
->dehydrateStateUsing(fn ($state, ?Listing $record): ?string => CountryCodeManager::normalizeStoredCountry($state ?? $record?->country)),
->options(fn (): array => Country::query()
->orderBy('name')
->pluck('name', 'name')
->all())
->searchable()
->preload()
->live()
->afterStateUpdated(fn ($state, $set) => $set('city', null))
->nullable(),
Select::make('city')
->label('City')
->options(function (Get $get): array {
$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()
->preload()
->nullable(),
Map::make('location')
->label('Location')
->visible(fn (): bool => ListingPanelHelper::googleMapsEnabled())
@ -90,15 +118,58 @@ class ListingResource extends Resource
TextColumn::make('id')->sortable(),
TextColumn::make('title')->searchable()->sortable()->limit(40),
TextColumn::make('category.name')->label('Category'),
TextColumn::make('user.email')->label('Owner')->searchable()->toggleable(),
TextColumn::make('price')
->currency(fn (Listing $record): string => $record->currency ?: ListingPanelHelper::defaultCurrency())
->sortable(),
StateFusionSelectColumn::make('status'),
IconColumn::make('is_featured')->boolean()->label('Featured'),
TextColumn::make('city'),
TextColumn::make('country'),
TextColumn::make('created_at')->dateTime()->sortable(),
])->filters([
StateFusionSelectFilter::make('status'),
SelectFilter::make('category_id')
->label('Category')
->relationship('category', 'name')
->searchable()
->preload(),
SelectFilter::make('user_id')
->label('Owner')
->relationship('user', 'email')
->searchable()
->preload(),
SelectFilter::make('country')
->options(fn (): array => Country::query()
->orderBy('name')
->pluck('name', 'name')
->all())
->searchable(),
SelectFilter::make('city')
->options(fn (): array => City::query()
->orderBy('name')
->pluck('name', 'name')
->all())
->searchable(),
TernaryFilter::make('is_featured')->label('Featured'),
Filter::make('created_at')
->label('Created Date')
->schema([
DatePicker::make('from')->label('From'),
DatePicker::make('until')->label('Until'),
])
->query(fn (Builder $query, array $data): Builder => $query
->when($data['from'] ?? null, fn (Builder $query, string $date): Builder => $query->whereDate('created_at', '>=', $date))
->when($data['until'] ?? null, fn (Builder $query, string $date): Builder => $query->whereDate('created_at', '<=', $date))),
Filter::make('price')
->label('Price Range')
->schema([
TextInput::make('min')->numeric()->label('Min'),
TextInput::make('max')->numeric()->label('Max'),
])
->query(fn (Builder $query, array $data): Builder => $query
->when($data['min'] ?? null, fn (Builder $query, string $amount): Builder => $query->where('price', '>=', (float) $amount))
->when($data['max'] ?? null, fn (Builder $query, string $amount): Builder => $query->where('price', '<=', (float) $amount))),
])->actions([
EditAction::make(),
Action::make('activities')

View File

@ -11,6 +11,7 @@ use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Modules\Admin\Filament\Resources\LocationResource\Pages;
use Modules\Location\Models\Country;
@ -22,6 +23,8 @@ class LocationResource extends Resource
protected static string | BackedEnum | null $navigationIcon = 'heroicon-o-globe-alt';
protected static string | UnitEnum | null $navigationGroup = 'Settings';
protected static ?string $label = 'Country';
protected static ?string $pluralLabel = 'Countries';
protected static ?int $navigationSort = 2;
public static function form(Schema $schema): Schema
{
@ -38,9 +41,13 @@ class LocationResource extends Resource
return $table->columns([
TextColumn::make('id')->sortable(),
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('code'),
TextColumn::make('code')->searchable()->sortable(),
TextColumn::make('phone_code'),
TextColumn::make('cities_count')->counts('cities')->label('Cities')->sortable(),
IconColumn::make('is_active')->boolean(),
TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true),
])->filters([
TernaryFilter::make('is_active')->label('Active'),
])->actions([
EditAction::make(),
Action::make('activities')

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace Modules\Category\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use Modules\Category\Models\Category;
use Illuminate\Auth\Access\HandlesAuthorization;
class CategoryPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Category');
}
public function view(AuthUser $authUser, Category $category): bool
{
return $authUser->can('View:Category');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Category');
}
public function update(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Update:Category');
}
public function delete(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Delete:Category');
}
public function restore(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Restore:Category');
}
public function forceDelete(AuthUser $authUser, Category $category): bool
{
return $authUser->can('ForceDelete:Category');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Category');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Category');
}
public function replicate(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Replicate:Category');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Category');
}
}

View File

@ -8,10 +8,23 @@ class ListingController extends Controller
{
public function index()
{
$search = trim((string) request('search', ''));
$listings = Listing::query()
->publicFeed()
->paginate(12);
return view('listing::index', compact('listings'));
->when($search !== '', function ($query) use ($search): void {
$query->where(function ($searchQuery) use ($search): void {
$searchQuery
->where('title', 'like', "%{$search}%")
->orWhere('description', 'like', "%{$search}%")
->orWhere('city', 'like', "%{$search}%")
->orWhere('country', 'like', "%{$search}%");
});
})
->paginate(12)
->withQueryString();
return view('listing::index', compact('listings', 'search'));
}
public function show(Listing $listing)

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace Modules\Listing\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use Modules\Listing\Models\Listing;
use Illuminate\Auth\Access\HandlesAuthorization;
class ListingPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Listing');
}
public function view(AuthUser $authUser, Listing $listing): bool
{
return $authUser->can('View:Listing');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Listing');
}
public function update(AuthUser $authUser, Listing $listing): bool
{
return $authUser->can('Update:Listing');
}
public function delete(AuthUser $authUser, Listing $listing): bool
{
return $authUser->can('Delete:Listing');
}
public function restore(AuthUser $authUser, Listing $listing): bool
{
return $authUser->can('Restore:Listing');
}
public function forceDelete(AuthUser $authUser, Listing $listing): bool
{
return $authUser->can('ForceDelete:Listing');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Listing');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Listing');
}
public function replicate(AuthUser $authUser, Listing $listing): bool
{
return $authUser->can('Replicate:Listing');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Listing');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace Modules\Location\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use Modules\Location\Models\Country;
use Illuminate\Auth\Access\HandlesAuthorization;
class CountryPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Country');
}
public function view(AuthUser $authUser, Country $country): bool
{
return $authUser->can('View:Country');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Country');
}
public function update(AuthUser $authUser, Country $country): bool
{
return $authUser->can('Update:Country');
}
public function delete(AuthUser $authUser, Country $country): bool
{
return $authUser->can('Delete:Country');
}
public function restore(AuthUser $authUser, Country $country): bool
{
return $authUser->can('Restore:Country');
}
public function forceDelete(AuthUser $authUser, Country $country): bool
{
return $authUser->can('ForceDelete:Country');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Country');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Country');
}
public function replicate(AuthUser $authUser, Country $country): bool
{
return $authUser->can('Replicate:Country');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Country');
}
}

View File

@ -46,10 +46,6 @@ class User extends Authenticatable implements FilamentUser, HasTenants, HasAvata
public function canAccessPanel(Panel $panel): bool
{
if ((string) $this->status !== 'active') {
return false;
}
return match ($panel->getId()) {
'admin' => $this->hasRole('admin'),
'partner' => true,

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\CmsCategory;
use Illuminate\Auth\Access\HandlesAuthorization;
class CmsCategoryPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CmsCategory');
}
public function view(AuthUser $authUser, CmsCategory $cmsCategory): bool
{
return $authUser->can('View:CmsCategory');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CmsCategory');
}
public function update(AuthUser $authUser, CmsCategory $cmsCategory): bool
{
return $authUser->can('Update:CmsCategory');
}
public function delete(AuthUser $authUser, CmsCategory $cmsCategory): bool
{
return $authUser->can('Delete:CmsCategory');
}
public function restore(AuthUser $authUser, CmsCategory $cmsCategory): bool
{
return $authUser->can('Restore:CmsCategory');
}
public function forceDelete(AuthUser $authUser, CmsCategory $cmsCategory): bool
{
return $authUser->can('ForceDelete:CmsCategory');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CmsCategory');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CmsCategory');
}
public function replicate(AuthUser $authUser, CmsCategory $cmsCategory): bool
{
return $authUser->can('Replicate:CmsCategory');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CmsCategory');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\CmsComment;
use Illuminate\Auth\Access\HandlesAuthorization;
class CmsCommentPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CmsComment');
}
public function view(AuthUser $authUser, CmsComment $cmsComment): bool
{
return $authUser->can('View:CmsComment');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CmsComment');
}
public function update(AuthUser $authUser, CmsComment $cmsComment): bool
{
return $authUser->can('Update:CmsComment');
}
public function delete(AuthUser $authUser, CmsComment $cmsComment): bool
{
return $authUser->can('Delete:CmsComment');
}
public function restore(AuthUser $authUser, CmsComment $cmsComment): bool
{
return $authUser->can('Restore:CmsComment');
}
public function forceDelete(AuthUser $authUser, CmsComment $cmsComment): bool
{
return $authUser->can('ForceDelete:CmsComment');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CmsComment');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CmsComment');
}
public function replicate(AuthUser $authUser, CmsComment $cmsComment): bool
{
return $authUser->can('Replicate:CmsComment');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CmsComment');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\CmsPage;
use Illuminate\Auth\Access\HandlesAuthorization;
class CmsPagePolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CmsPage');
}
public function view(AuthUser $authUser, CmsPage $cmsPage): bool
{
return $authUser->can('View:CmsPage');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CmsPage');
}
public function update(AuthUser $authUser, CmsPage $cmsPage): bool
{
return $authUser->can('Update:CmsPage');
}
public function delete(AuthUser $authUser, CmsPage $cmsPage): bool
{
return $authUser->can('Delete:CmsPage');
}
public function restore(AuthUser $authUser, CmsPage $cmsPage): bool
{
return $authUser->can('Restore:CmsPage');
}
public function forceDelete(AuthUser $authUser, CmsPage $cmsPage): bool
{
return $authUser->can('ForceDelete:CmsPage');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CmsPage');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CmsPage');
}
public function replicate(AuthUser $authUser, CmsPage $cmsPage): bool
{
return $authUser->can('Replicate:CmsPage');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CmsPage');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\CmsPost;
use Illuminate\Auth\Access\HandlesAuthorization;
class CmsPostPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CmsPost');
}
public function view(AuthUser $authUser, CmsPost $cmsPost): bool
{
return $authUser->can('View:CmsPost');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CmsPost');
}
public function update(AuthUser $authUser, CmsPost $cmsPost): bool
{
return $authUser->can('Update:CmsPost');
}
public function delete(AuthUser $authUser, CmsPost $cmsPost): bool
{
return $authUser->can('Delete:CmsPost');
}
public function restore(AuthUser $authUser, CmsPost $cmsPost): bool
{
return $authUser->can('Restore:CmsPost');
}
public function forceDelete(AuthUser $authUser, CmsPost $cmsPost): bool
{
return $authUser->can('ForceDelete:CmsPost');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CmsPost');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CmsPost');
}
public function replicate(AuthUser $authUser, CmsPost $cmsPost): bool
{
return $authUser->can('Replicate:CmsPost');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CmsPost');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\MediaCollection;
use Illuminate\Auth\Access\HandlesAuthorization;
class MediaCollectionPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:MediaCollection');
}
public function view(AuthUser $authUser, MediaCollection $mediaCollection): bool
{
return $authUser->can('View:MediaCollection');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:MediaCollection');
}
public function update(AuthUser $authUser, MediaCollection $mediaCollection): bool
{
return $authUser->can('Update:MediaCollection');
}
public function delete(AuthUser $authUser, MediaCollection $mediaCollection): bool
{
return $authUser->can('Delete:MediaCollection');
}
public function restore(AuthUser $authUser, MediaCollection $mediaCollection): bool
{
return $authUser->can('Restore:MediaCollection');
}
public function forceDelete(AuthUser $authUser, MediaCollection $mediaCollection): bool
{
return $authUser->can('ForceDelete:MediaCollection');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:MediaCollection');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:MediaCollection');
}
public function replicate(AuthUser $authUser, MediaCollection $mediaCollection): bool
{
return $authUser->can('Replicate:MediaCollection');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:MediaCollection');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use Spatie\Permission\Models\Role;
use Illuminate\Auth\Access\HandlesAuthorization;
class RolePolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Role');
}
public function view(AuthUser $authUser, Role $role): bool
{
return $authUser->can('View:Role');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Role');
}
public function update(AuthUser $authUser, Role $role): bool
{
return $authUser->can('Update:Role');
}
public function delete(AuthUser $authUser, Role $role): bool
{
return $authUser->can('Delete:Role');
}
public function restore(AuthUser $authUser, Role $role): bool
{
return $authUser->can('Restore:Role');
}
public function forceDelete(AuthUser $authUser, Role $role): bool
{
return $authUser->can('ForceDelete:Role');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Role');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Role');
}
public function replicate(AuthUser $authUser, Role $role): bool
{
return $authUser->can('Replicate:Role');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Role');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\TallcmsContactSubmission;
use Illuminate\Auth\Access\HandlesAuthorization;
class TallcmsContactSubmissionPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:TallcmsContactSubmission');
}
public function view(AuthUser $authUser, TallcmsContactSubmission $tallcmsContactSubmission): bool
{
return $authUser->can('View:TallcmsContactSubmission');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:TallcmsContactSubmission');
}
public function update(AuthUser $authUser, TallcmsContactSubmission $tallcmsContactSubmission): bool
{
return $authUser->can('Update:TallcmsContactSubmission');
}
public function delete(AuthUser $authUser, TallcmsContactSubmission $tallcmsContactSubmission): bool
{
return $authUser->can('Delete:TallcmsContactSubmission');
}
public function restore(AuthUser $authUser, TallcmsContactSubmission $tallcmsContactSubmission): bool
{
return $authUser->can('Restore:TallcmsContactSubmission');
}
public function forceDelete(AuthUser $authUser, TallcmsContactSubmission $tallcmsContactSubmission): bool
{
return $authUser->can('ForceDelete:TallcmsContactSubmission');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:TallcmsContactSubmission');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:TallcmsContactSubmission');
}
public function replicate(AuthUser $authUser, TallcmsContactSubmission $tallcmsContactSubmission): bool
{
return $authUser->can('Replicate:TallcmsContactSubmission');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:TallcmsContactSubmission');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\TallcmsMedia;
use Illuminate\Auth\Access\HandlesAuthorization;
class TallcmsMediaPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:TallcmsMedia');
}
public function view(AuthUser $authUser, TallcmsMedia $tallcmsMedia): bool
{
return $authUser->can('View:TallcmsMedia');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:TallcmsMedia');
}
public function update(AuthUser $authUser, TallcmsMedia $tallcmsMedia): bool
{
return $authUser->can('Update:TallcmsMedia');
}
public function delete(AuthUser $authUser, TallcmsMedia $tallcmsMedia): bool
{
return $authUser->can('Delete:TallcmsMedia');
}
public function restore(AuthUser $authUser, TallcmsMedia $tallcmsMedia): bool
{
return $authUser->can('Restore:TallcmsMedia');
}
public function forceDelete(AuthUser $authUser, TallcmsMedia $tallcmsMedia): bool
{
return $authUser->can('ForceDelete:TallcmsMedia');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:TallcmsMedia');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:TallcmsMedia');
}
public function replicate(AuthUser $authUser, TallcmsMedia $tallcmsMedia): bool
{
return $authUser->can('Replicate:TallcmsMedia');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:TallcmsMedia');
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use TallCms\Cms\Models\TallcmsMenu;
use Illuminate\Auth\Access\HandlesAuthorization;
class TallcmsMenuPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:TallcmsMenu');
}
public function view(AuthUser $authUser, TallcmsMenu $tallcmsMenu): bool
{
return $authUser->can('View:TallcmsMenu');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:TallcmsMenu');
}
public function update(AuthUser $authUser, TallcmsMenu $tallcmsMenu): bool
{
return $authUser->can('Update:TallcmsMenu');
}
public function delete(AuthUser $authUser, TallcmsMenu $tallcmsMenu): bool
{
return $authUser->can('Delete:TallcmsMenu');
}
public function restore(AuthUser $authUser, TallcmsMenu $tallcmsMenu): bool
{
return $authUser->can('Restore:TallcmsMenu');
}
public function forceDelete(AuthUser $authUser, TallcmsMenu $tallcmsMenu): bool
{
return $authUser->can('ForceDelete:TallcmsMenu');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:TallcmsMenu');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:TallcmsMenu');
}
public function replicate(AuthUser $authUser, TallcmsMenu $tallcmsMenu): bool
{
return $authUser->can('Replicate:TallcmsMenu');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:TallcmsMenu');
}
}

View File

@ -1,67 +0,0 @@
<?php
namespace App\Policies;
use Illuminate\Foundation\Auth\User as AuthUser;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:User');
}
public function view(AuthUser $authUser): bool
{
return $authUser->can('View:User');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:User');
}
public function update(AuthUser $authUser): bool
{
return $authUser->can('Update:User');
}
public function delete(AuthUser $authUser): bool
{
return $authUser->can('Delete:User');
}
public function restore(AuthUser $authUser): bool
{
return $authUser->can('Restore:User');
}
public function forceDelete(AuthUser $authUser): bool
{
return $authUser->can('ForceDelete:User');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:User');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:User');
}
public function replicate(AuthUser $authUser): bool
{
return $authUser->can('Replicate:User');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:User');
}
}

View File

@ -7,6 +7,7 @@ use App\Settings\GeneralSettings;
use BezhanSalleh\LanguageSwitch\LanguageSwitch;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
@ -23,6 +24,14 @@ class AppServiceProvider extends ServiceProvider
public function boot(): void
{
Gate::before(function ($user): null | bool {
if (method_exists($user, 'hasRole') && $user->hasRole('admin')) {
return true;
}
return null;
});
Route::pattern('tenant', '[0-9]+');
View::addNamespace('app', resource_path('views'));

View File

@ -1,95 +1,210 @@
@extends('app::layouts.app')
@section('content')
<div class="bg-gradient-to-br from-blue-600 to-blue-800 text-white py-16">
<div class="container mx-auto px-4 text-center">
<h1 class="text-4xl md:text-5xl font-bold mb-4">{{ __('messages.find_what_you_need') }}</h1>
<p class="text-blue-200 text-lg mb-8">{{ __('messages.hero_subtitle') }}</p>
<form action="{{ route('listings.index') }}" method="GET" class="max-w-2xl mx-auto">
<div class="flex bg-white rounded-xl overflow-hidden shadow-lg">
<input type="text" name="search" placeholder="{{ __('messages.search_placeholder') }}" class="flex-1 px-5 py-4 text-gray-800 focus:outline-none text-lg">
<button type="submit" class="bg-orange-500 hover:bg-orange-600 text-white px-8 py-4 font-semibold transition">{{ __('messages.search') }}</button>
@php
$menuCategories = $categories->take(8);
$heroListing = $featuredListings->first() ?? $recentListings->first();
$heroImage = $heroListing?->getFirstMediaUrl('listing-images');
$listingCards = $recentListings->take(6);
$trendGradients = [
'from-emerald-500 to-teal-600',
'from-rose-500 to-pink-600',
'from-fuchsia-500 to-rose-600',
'from-sky-500 to-blue-600',
'from-amber-500 to-orange-600',
'from-cyan-500 to-indigo-600',
'from-red-500 to-rose-600',
'from-violet-500 to-purple-600',
];
@endphp
<div class="max-w-[1320px] mx-auto px-4 py-5 md:py-7 space-y-7">
<section class="bg-white border border-slate-200 rounded-2xl px-2 py-2 overflow-x-auto">
<div class="flex items-center gap-2 min-w-max">
<a href="{{ route('categories.index') }}" class="inline-flex items-center gap-2 px-4 py-2.5 rounded-full bg-slate-900 text-white text-sm font-semibold">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
Tüm Kategoriler
</a>
@foreach($menuCategories as $category)
<a href="{{ route('categories.show', $category) }}" class="px-4 py-2.5 rounded-full text-sm font-medium text-slate-700 hover:bg-slate-100 transition whitespace-nowrap">
{{ $category->name }}
</a>
@endforeach
<a href="{{ route('listings.index') }}" class="px-4 py-2.5 rounded-full text-sm font-medium text-slate-700 hover:bg-slate-100 transition whitespace-nowrap">
{{ __('messages.listings') }}
</a>
</div>
</form>
</section>
<section class="relative overflow-hidden rounded-[28px] bg-gradient-to-r from-blue-900 via-blue-700 to-blue-600 text-white shadow-xl">
<div class="absolute -top-20 -left-24 w-80 h-80 rounded-full bg-blue-400/20 blur-3xl"></div>
<div class="absolute -bottom-24 right-10 w-80 h-80 rounded-full bg-cyan-300/20 blur-3xl"></div>
<div class="relative grid lg:grid-cols-[1fr,1.1fr] gap-6 items-center px-8 md:px-12 py-12 md:py-14">
<div>
<p class="text-sm uppercase tracking-[0.22em] text-blue-200 font-semibold mb-4">OpenClassify Marketplace</p>
<h1 class="text-4xl md:text-5xl leading-tight font-extrabold max-w-xl">
İlan ücreti ödemeden ürününü hızla sat!
</h1>
<p class="mt-4 text-blue-100 text-base md:text-lg max-w-xl">
{{ __('messages.hero_subtitle') }}
</p>
<div class="mt-8 flex flex-wrap items-center gap-3">
<a href="{{ route('listings.index') }}" class="bg-white text-blue-900 px-8 py-3 rounded-full font-semibold hover:bg-blue-50 transition">
İncele
</a>
@auth
<a href="{{ route('filament.partner.resources.listings.create', ['tenant' => auth()->id()]) }}" class="border border-blue-200/60 px-8 py-3 rounded-full font-semibold hover:bg-white/10 transition">
{{ __('messages.post_listing') }}
</a>
@else
<a href="{{ route('filament.partner.auth.login') }}" class="border border-blue-200/60 px-8 py-3 rounded-full font-semibold hover:bg-white/10 transition">
{{ __('messages.post_listing') }}
</a>
@endauth
</div>
<div class="mt-8 flex items-center gap-2">
<span class="w-2.5 h-2.5 rounded-full bg-white/40"></span>
<span class="w-2.5 h-2.5 rounded-full bg-white/40"></span>
<span class="w-7 h-2.5 rounded-full bg-white"></span>
<span class="w-2.5 h-2.5 rounded-full bg-white/40"></span>
</div>
</div>
<div class="bg-white border-b py-4">
<div class="container mx-auto px-4">
<div class="flex justify-center space-x-8 text-center text-sm text-gray-600">
<div><span class="font-bold text-blue-600 text-lg">{{ $listingCount ?? 0 }}</span><br>Active Listings</div>
<div><span class="font-bold text-blue-600 text-lg">{{ $categoryCount ?? 0 }}</span><br>Categories</div>
<div><span class="font-bold text-blue-600 text-lg">{{ $userCount ?? 0 }}</span><br>Users</div>
<div class="relative h-[310px] md:h-[360px]">
<div class="absolute left-6 md:left-10 bottom-0 w-32 md:w-40 h-[250px] md:h-[300px] bg-slate-950 rounded-[32px] shadow-2xl p-2 rotate-[-8deg]">
<div class="w-full h-full rounded-[24px] bg-white overflow-hidden">
<div class="px-3 py-2 border-b border-slate-100">
<p class="text-rose-500 text-sm font-bold">OpenClassify</p>
<p class="text-[10px] text-slate-400 mt-1">Ürün, kategori, satıcı ara</p>
</div>
<div class="p-2 space-y-2">
<div class="h-10 rounded-xl bg-slate-100"></div>
<div class="grid grid-cols-3 gap-2">
<div class="h-9 rounded-lg bg-blue-100"></div>
<div class="h-9 rounded-lg bg-emerald-100"></div>
<div class="h-9 rounded-lg bg-amber-100"></div>
</div>
<div class="space-y-2">
<div class="h-14 rounded-xl bg-slate-100"></div>
<div class="h-14 rounded-xl bg-slate-100"></div>
</div>
</div>
</div>
<div class="container mx-auto px-4 py-12">
<h2 class="text-2xl font-bold mb-6">{{ __('messages.browse_categories') }}</h2>
<div class="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-4">
@foreach($categories as $category)
<a href="{{ route('categories.show', $category) }}" class="bg-white rounded-xl p-4 text-center shadow-sm hover:shadow-md transition hover:-translate-y-1 transform">
<div class="text-3xl mb-2">{{ $category->icon ?? '📦' }}</div>
<div class="text-xs font-medium text-gray-700 truncate">{{ $category->name }}</div>
</div>
<div class="absolute right-0 bottom-0 w-[78%] h-[88%] rounded-[28px] bg-gradient-to-br from-white/20 to-blue-500/40 border border-white/20 shadow-2xl flex items-end justify-center p-4">
@if($heroImage)
<img src="{{ $heroImage }}" alt="{{ $heroListing?->title }}" class="w-full h-full object-cover rounded-2xl">
@else
<div class="w-full h-full rounded-2xl bg-white/90 text-slate-800 flex flex-col justify-center items-center gap-3">
<span class="text-6xl">🚗</span>
<p class="text-sm font-semibold px-4 text-center">Görsel eklendiğinde burada öne çıkan ilan yer alacak.</p>
</div>
@endif
</div>
</div>
</div>
</section>
<section>
<div class="flex items-center justify-between mb-4">
<h2 class="text-2xl font-bold text-slate-900">Trend Kategoriler</h2>
<a href="{{ route('categories.index') }}" class="text-sm font-semibold text-rose-500 hover:text-rose-600 transition">Tümünü Gör</a>
</div>
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-8 gap-3">
@foreach($menuCategories as $index => $category)
<a href="{{ route('categories.show', $category) }}" class="group rounded-2xl overflow-hidden bg-white border border-slate-200 hover:shadow-md transition">
<div class="h-20 bg-gradient-to-r {{ $trendGradients[$index % count($trendGradients)] }} relative">
<span class="absolute -bottom-4 right-4 text-3xl drop-shadow">{{ $category->icon ?? '📦' }}</span>
</div>
<div class="px-3 py-3">
<p class="text-xs font-semibold text-slate-800 leading-tight h-9 overflow-hidden">{{ $category->name }}</p>
</div>
</a>
@endforeach
</div>
<div class="text-center mt-6">
<a href="{{ route('categories.index') }}" class="text-blue-600 hover:underline font-medium">View all categories </a>
</div>
</div>
@if($featuredListings->count())
<div class="bg-gray-100 py-12">
<div class="container mx-auto px-4">
<h2 class="text-2xl font-bold mb-6"> Featured Listings</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
@foreach($featuredListings as $listing)
<div class="bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition">
<div class="bg-gradient-to-br from-blue-100 to-blue-200 h-48 flex items-center justify-center">
<svg class="w-16 h-16 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
<div class="p-4">
<h3 class="font-semibold text-gray-900 truncate">{{ $listing->title }}</h3>
<p class="text-green-600 font-bold text-lg">{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}</p>
<p class="text-gray-500 text-sm">{{ $listing->city }}</p>
<a href="{{ route('listings.show', $listing) }}" class="mt-3 block text-center bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition text-sm">View Details</a>
</div>
</div>
@endforeach
</section>
<section>
<div class="flex items-center justify-between mb-4">
<h2 class="text-2xl font-bold text-slate-900">Popüler İkinci El İlanlar</h2>
<div class="hidden sm:flex items-center gap-2 text-sm text-slate-500">
<span class="w-8 h-8 rounded-full border border-slate-300 grid place-items-center"></span>
<span class="w-8 h-8 rounded-full border border-slate-300 grid place-items-center"></span>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
@forelse($listingCards as $listing)
@php
$listingImage = $listing->getFirstMediaUrl('listing-images');
$priceLabel = $listing->price ? number_format((float) $listing->price, 0).' '.$listing->currency : __('messages.free');
$locationLabel = trim(collect([$listing->city, $listing->country])->filter()->join(', '));
@endphp
<article class="rounded-2xl border border-slate-200 bg-white overflow-hidden shadow-sm hover:shadow-md transition">
<div class="relative h-64 md:h-[290px] bg-slate-100">
@if($listingImage)
<img src="{{ $listingImage }}" alt="{{ $listing->title }}" class="w-full h-full object-cover">
@else
<div class="w-full h-full grid place-items-center text-slate-400">
<svg class="w-14 h-14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.7" d="M4 16l4.5-4.5a2 2 0 012.8 0L16 16m-1.5-1.5l1.8-1.8a2 2 0 012.8 0L21 14m-7-8h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
</div>
@endif
<div class="container mx-auto px-4 py-12">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">{{ __('messages.recent_listings') }}</h2>
<a href="{{ route('listings.index') }}" class="text-blue-600 hover:underline font-medium">View all </a>
<div class="absolute top-3 left-3 flex items-center gap-2">
@if($listing->is_featured)
<span class="bg-amber-300 text-amber-950 text-xs font-bold px-2.5 py-1 rounded-full">Öne Çıkan</span>
@endif
<span class="bg-sky-500 text-white text-xs font-semibold px-2.5 py-1 rounded-full">Büyük İlan</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
@foreach($recentListings as $listing)
<div class="bg-white rounded-xl shadow-sm overflow-hidden hover:shadow-md transition border border-gray-100">
<div class="bg-gray-100 h-44 flex items-center justify-center">
<svg class="w-12 h-12 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
<button type="button" class="absolute top-3 right-3 w-9 h-9 rounded-full bg-white/90 text-slate-500 grid place-items-center hover:text-rose-500 transition"></button>
</div>
<div class="p-4">
<h3 class="font-medium text-gray-900 truncate text-sm">{{ $listing->title }}</h3>
<p class="text-green-600 font-bold">{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}</p>
<div class="flex justify-between items-center mt-2">
<p class="text-gray-400 text-xs">{{ $listing->city }}</p>
<p class="text-gray-400 text-xs">{{ $listing->created_at->diffForHumans() }}</p>
<div class="rounded-lg bg-emerald-50 text-emerald-700 text-xs font-semibold px-3 py-1.5 text-center mb-3">
Elden al, kartla öde!
</div>
<a href="{{ route('listings.show', $listing) }}" class="mt-2 block text-center border border-blue-600 text-blue-600 py-1.5 rounded-lg hover:bg-blue-600 hover:text-white transition text-sm">View</a>
<div class="flex items-start justify-between gap-3">
<div>
<p class="text-3xl font-extrabold tracking-tight text-slate-900">{{ $priceLabel }}</p>
<h3 class="text-xl font-semibold text-slate-800 mt-1 truncate">{{ $listing->title }}</h3>
</div>
<span class="text-xs text-blue-600 bg-blue-50 px-2 py-1 rounded-full font-semibold">12 taksit</span>
</div>
@endforeach
<div class="mt-5 flex items-center justify-between text-sm text-slate-500">
<span class="truncate">{{ $locationLabel !== '' ? $locationLabel : 'Konum belirtilmedi' }}</span>
<span>{{ $listing->created_at->diffForHumans() }}</span>
</div>
<a href="{{ route('listings.show', $listing) }}" class="mt-4 inline-flex items-center gap-2 text-sm font-semibold text-blue-700 hover:text-blue-900 transition">
İlan detayını
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M9 5l7 7-7 7"/>
</svg>
</a>
</div>
</article>
@empty
<div class="col-span-2 border border-dashed border-slate-300 bg-white rounded-2xl py-20 text-center text-slate-500">
Henüz ilan bulunmuyor.
</div>
@endforelse
</div>
</section>
<section class="rounded-3xl bg-slate-900 text-white px-8 py-10 md:p-12">
<div class="grid md:grid-cols-[1fr,auto] gap-6 items-center">
<div>
<h2 class="text-3xl md:text-4xl font-extrabold">{{ __('messages.sell_something') }}</h2>
<p class="text-slate-300 mt-3">Dakikalar içinde ücretsiz ilan oluştur, binlerce alıcıya ulaş.</p>
</div>
<div class="bg-blue-600 text-white py-12 mt-8">
<div class="container mx-auto px-4 text-center">
<h2 class="text-3xl font-bold mb-4">{{ __('messages.sell_something') }}</h2>
<p class="text-blue-200 mb-6">Post your first listing for free!</p>
@auth
<a href="{{ route('filament.partner.resources.listings.create', ['tenant' => auth()->id()]) }}" class="bg-orange-500 text-white px-8 py-3 rounded-xl hover:bg-orange-600 transition font-semibold text-lg">Post a Free Ad</a>
<a href="{{ route('filament.partner.resources.listings.create', ['tenant' => auth()->id()]) }}" class="inline-flex items-center justify-center rounded-full bg-rose-500 hover:bg-rose-600 px-8 py-3 font-semibold transition whitespace-nowrap">
Hemen İlan Ver
</a>
@else
<a href="{{ route('register') }}" class="bg-white text-blue-600 px-8 py-3 rounded-xl hover:bg-gray-100 transition font-semibold text-lg">Get Started Free</a>
<a href="{{ route('register') }}" class="inline-flex items-center justify-center rounded-full bg-white text-slate-900 hover:bg-slate-100 px-8 py-3 font-semibold transition whitespace-nowrap">
Ücretsiz Başla
</a>
@endauth
</div>
</section>
</div>
@endsection

View File

@ -10,12 +10,24 @@
$partnerLoginRoute = route('filament.partner.auth.login');
$partnerRegisterRoute = route('register');
$partnerLogoutRoute = route('filament.partner.auth.logout');
$partnerCreateRoute = auth()->check()
? route('filament.partner.resources.listings.create', ['tenant' => auth()->id()])
: $partnerLoginRoute;
$partnerCreateRoute = route('partner.listings.create');
$partnerDashboardRoute = auth()->check()
? route('filament.partner.pages.dashboard', ['tenant' => auth()->id()])
: $partnerLoginRoute;
$availableLocales = config('app.available_locales', ['en']);
$localeLabels = [
'en' => 'English',
'tr' => 'Türkçe',
'ar' => 'العربية',
'zh' => '中文',
'es' => 'Español',
'fr' => 'Français',
'de' => 'Deutsch',
'pt' => 'Português',
'ru' => 'Русский',
'ja' => '日本語',
];
$isHomePage = request()->routeIs('home');
@endphp
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" dir="{{ in_array(app()->getLocale(), ['ar']) ? 'rtl' : 'ltr' }}">
@ -25,89 +37,184 @@
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ $siteName }} @hasSection('title') - @yield('title') @endif</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>body { font-family: 'Inter', sans-serif; } [dir="rtl"] { text-align: right; }</style>
<link href="https://fonts.googleapis.com/css2?family=Pacifico&family=Sora:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root {
--oc-bg: #f5f6fa;
--oc-surface: #ffffff;
--oc-border: #e4e7ef;
--oc-text: #191e2b;
--oc-muted: #667085;
--oc-primary: #ff4365;
--oc-primary-soft: #ffe7ed;
--oc-chip: #f1f3f8;
}
body {
font-family: 'Sora', sans-serif;
background: radial-gradient(circle at top right, #fce6ef 0%, #f5f6fa 28%);
color: var(--oc-text);
}
.brand-mark {
font-family: 'Pacifico', cursive;
}
.market-nav-surface {
background: rgba(255, 255, 255, 0.88);
backdrop-filter: saturate(180%) blur(8px);
border-bottom: 1px solid var(--oc-border);
}
.search-shell {
border: 1px solid #d9ddea;
background: #fbfcff;
border-radius: 999px;
}
.chip-btn {
border: 1px solid #d9ddea;
background: var(--oc-chip);
border-radius: 999px;
}
.btn-primary {
background: linear-gradient(120deg, #ff516e, #ff2f57);
color: #fff;
border-radius: 999px;
}
[dir="rtl"] {
text-align: right;
}
</style>
</head>
<body class="bg-gray-50">
<nav class="bg-white shadow-sm border-b sticky top-0 z-50">
<div class="container mx-auto px-4">
<div class="flex items-center justify-between h-16">
<a href="{{ route('home') }}" class="text-2xl font-bold text-blue-600 flex items-center gap-2">
<body class="min-h-screen">
<nav class="market-nav-surface sticky top-0 z-50">
<div class="max-w-[1320px] mx-auto px-4 py-4">
<div class="flex items-center gap-3 md:gap-4">
<a href="{{ route('home') }}" class="shrink-0 flex items-center gap-2">
@if($siteLogoUrl)
<img src="{{ $siteLogoUrl }}" alt="{{ $siteName }}" class="h-9 w-auto rounded">
@endif
<span>{{ $siteName }}</span>
<span class="brand-mark text-3xl text-rose-500 leading-none">{{ $siteName }}</span>
</a>
<div class="hidden md:flex items-center space-x-6">
<a href="{{ route('home') }}" class="text-gray-600 hover:text-blue-600 transition">{{ __('messages.home') }}</a>
<a href="{{ route('categories.index') }}" class="text-gray-600 hover:text-blue-600 transition">{{ __('messages.categories') }}</a>
<a href="{{ route('listings.index') }}" class="text-gray-600 hover:text-blue-600 transition">{{ __('messages.listings') }}</a>
</div>
<div class="flex items-center space-x-4">
<div class="relative group">
<button class="flex items-center text-gray-600 hover:text-blue-600 transition px-2 py-1 rounded border text-sm">
🌐 {{ strtoupper(app()->getLocale()) }}
<form action="{{ route('listings.index') }}" method="GET" class="hidden lg:flex flex-1 search-shell items-center gap-2 px-4 py-2.5">
<svg class="w-5 h-5 text-rose-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M21 21l-4.35-4.35m1.6-5.05a7.25 7.25 0 11-14.5 0 7.25 7.25 0 0114.5 0z"/>
</svg>
<input
type="text"
name="search"
value="{{ request('search') }}"
placeholder="{{ __('messages.search_placeholder') }}"
class="w-full bg-transparent text-sm text-slate-700 placeholder:text-slate-400 focus:outline-none"
>
<button type="submit" class="text-xs font-semibold text-slate-500 hover:text-slate-700 transition">
{{ __('messages.search') }}
</button>
<div class="absolute right-0 mt-1 bg-white shadow-lg rounded-lg border hidden group-hover:block z-50 w-32">
@foreach(config('app.available_locales', ['en']) as $locale)
<a href="{{ route('lang.switch', $locale) }}" class="block px-4 py-2 text-sm hover:bg-gray-50 {{ app()->getLocale() === $locale ? 'font-bold text-blue-600' : 'text-gray-700' }}">
{{ ['en'=>'English','tr'=>'Türkçe','ar'=>'العربية','zh'=>'中文','es'=>'Español','fr'=>'Français','de'=>'Deutsch','pt'=>'Português','ru'=>'Русский','ja'=>'日本語'][$locale] ?? $locale }}
</form>
<button type="button" class="chip-btn hidden md:flex items-center gap-2 px-4 py-2 text-sm text-slate-700">
<svg class="w-4 h-4 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M12 21s7-6.2 7-11a7 7 0 10-14 0c0 4.8 7 11 7 11z"/>
<circle cx="12" cy="10" r="2.3" stroke-width="1.8" />
</svg>
<span>Istanbul, Türkiye</span>
<svg class="w-4 h-4 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M6 9l6 6 6-6"/>
</svg>
</button>
<div class="ml-auto flex items-center gap-2 md:gap-3">
<details class="relative">
<summary class="chip-btn list-none cursor-pointer px-3 py-2 text-xs md:text-sm text-slate-700">
{{ strtoupper(app()->getLocale()) }}
</summary>
<div class="absolute right-0 mt-2 bg-white border border-slate-200 shadow-lg rounded-xl overflow-hidden min-w-28">
@foreach($availableLocales as $locale)
<a href="{{ route('lang.switch', $locale) }}" class="block px-3 py-2 text-sm hover:bg-slate-50 {{ app()->getLocale() === $locale ? 'font-semibold text-rose-500' : 'text-slate-700' }}">
{{ $localeLabels[$locale] ?? strtoupper($locale) }}
</a>
@endforeach
</div>
</div>
</details>
@auth
<a href="{{ $partnerCreateRoute }}" class="bg-orange-500 text-white px-4 py-2 rounded-lg hover:bg-orange-600 transition font-medium text-sm">+ Post Ad</a>
<div class="relative group">
<button class="text-gray-600 hover:text-blue-600">{{ auth()->user()->name }}</button>
<div class="absolute right-0 mt-1 bg-white shadow-lg rounded-lg border hidden group-hover:block z-50 w-40">
<a href="{{ route('profile.show') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50">My Profile</a>
<a href="{{ $partnerDashboardRoute }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50">Dashboard</a>
<form method="POST" action="{{ $partnerLogoutRoute }}">
<a href="{{ $partnerDashboardRoute }}" class="hidden sm:inline-flex text-sm font-medium text-slate-600 hover:text-slate-900 transition">Panel</a>
<a href="{{ $partnerCreateRoute }}" class="btn-primary px-4 md:px-5 py-2 text-sm font-semibold shadow-sm hover:brightness-95 transition">
+ {{ __('messages.post_listing') }}
</a>
<form method="POST" action="{{ $partnerLogoutRoute }}" class="hidden sm:block">
@csrf
<button type="submit" class="w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-gray-50">Logout</button>
<button type="submit" class="text-sm text-slate-500 hover:text-rose-500 transition">{{ __('messages.logout') }}</button>
</form>
</div>
</div>
@else
<a href="{{ $partnerLoginRoute }}" class="text-gray-600 hover:text-blue-600 transition">{{ __('messages.login') }}</a>
<a href="{{ $partnerRegisterRoute }}" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition text-sm">{{ __('messages.register') }}</a>
<a href="{{ $partnerLoginRoute }}" class="bg-rose-50 text-rose-500 px-4 md:px-5 py-2 rounded-full text-sm font-semibold hover:bg-rose-100 transition">
{{ __('messages.login') }}
</a>
<a href="{{ $partnerCreateRoute }}" class="btn-primary px-4 md:px-5 py-2 text-sm font-semibold shadow-sm hover:brightness-95 transition">
{{ __('messages.post_listing') }}
</a>
@endauth
</div>
</div>
<div class="mt-3 lg:hidden">
<form action="{{ route('listings.index') }}" method="GET" class="search-shell flex items-center gap-2 px-3 py-2.5">
<svg class="w-4 h-4 text-rose-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" d="M21 21l-4.35-4.35m1.6-5.05a7.25 7.25 0 11-14.5 0 7.25 7.25 0 0114.5 0z"/>
</svg>
<input
type="text"
name="search"
value="{{ request('search') }}"
placeholder="{{ __('messages.search_placeholder') }}"
class="w-full bg-transparent text-sm text-slate-700 placeholder:text-slate-400 focus:outline-none"
>
<button type="submit" class="text-xs text-slate-500">{{ __('messages.search') }}</button>
</form>
</div>
@if(! $isHomePage)
<div class="mt-3 flex items-center gap-2 text-sm overflow-x-auto pb-1">
<a href="{{ route('home') }}" class="chip-btn whitespace-nowrap px-4 py-2 hover:bg-slate-100 transition">{{ __('messages.home') }}</a>
<a href="{{ route('categories.index') }}" class="chip-btn whitespace-nowrap px-4 py-2 hover:bg-slate-100 transition">{{ __('messages.categories') }}</a>
<a href="{{ route('listings.index') }}" class="chip-btn whitespace-nowrap px-4 py-2 hover:bg-slate-100 transition">{{ __('messages.listings') }}</a>
</div>
@endif
</div>
</nav>
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 text-center">{{ session('success') }}</div>
<div class="max-w-[1320px] mx-auto px-4 pt-3">
<div class="bg-emerald-100 border border-emerald-300 text-emerald-800 px-4 py-3 rounded-xl text-sm">{{ session('success') }}</div>
</div>
@endif
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 text-center">{{ session('error') }}</div>
<div class="max-w-[1320px] mx-auto px-4 pt-3">
<div class="bg-rose-100 border border-rose-300 text-rose-700 px-4 py-3 rounded-xl text-sm">{{ session('error') }}</div>
</div>
@endif
<main>@yield('content')</main>
<footer class="bg-gray-800 text-gray-400 mt-16 py-12">
<div class="container mx-auto px-4">
<footer class="mt-14 bg-slate-900 text-slate-300">
<div class="max-w-[1320px] mx-auto px-4 py-12">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 class="text-white font-bold text-lg mb-4">{{ $siteName }}</h3>
<p class="text-sm">{{ $siteDescription }}</p>
<h3 class="text-white font-semibold text-lg mb-3">{{ $siteName }}</h3>
<p class="text-sm text-slate-400 leading-relaxed">{{ $siteDescription }}</p>
</div>
<div>
<h4 class="text-white font-medium mb-4">Quick Links</h4>
<h4 class="text-white font-medium mb-4">Hızlı Linkler</h4>
<ul class="space-y-2 text-sm">
<li><a href="{{ route('home') }}" class="hover:text-white transition">Home</a></li>
<li><a href="{{ route('categories.index') }}" class="hover:text-white transition">Categories</a></li>
<li><a href="{{ route('listings.index') }}" class="hover:text-white transition">All Listings</a></li>
<li><a href="{{ route('home') }}" class="hover:text-white transition">Ana Sayfa</a></li>
<li><a href="{{ route('categories.index') }}" class="hover:text-white transition">Kategoriler</a></li>
<li><a href="{{ route('listings.index') }}" class="hover:text-white transition">Tüm İlanlar</a></li>
</ul>
</div>
<div>
<h4 class="text-white font-medium mb-4">Account</h4>
<h4 class="text-white font-medium mb-4">Hesap</h4>
<ul class="space-y-2 text-sm">
<li><a href="{{ $partnerLoginRoute }}" class="hover:text-white transition">Login</a></li>
<li><a href="{{ $partnerRegisterRoute }}" class="hover:text-white transition">Register</a></li>
<li><a href="{{ $partnerLoginRoute }}" class="hover:text-white transition">{{ __('messages.login') }}</a></li>
<li><a href="{{ $partnerRegisterRoute }}" class="hover:text-white transition">{{ __('messages.register') }}</a></li>
</ul>
</div>
<div>
<h4 class="text-white font-medium mb-4">Connect</h4>
<h4 class="text-white font-medium mb-4">Bağlantılar</h4>
<ul class="space-y-2 text-sm mb-4">
@if($linkedinUrl)
<li><a href="{{ $linkedinUrl }}" target="_blank" rel="noopener" class="hover:text-white transition">LinkedIn</a></li>
@ -119,18 +226,18 @@
<li><a href="{{ $whatsappUrl }}" target="_blank" rel="noopener" class="hover:text-white transition">WhatsApp</a></li>
@endif
@if(!$linkedinUrl && !$instagramUrl && !$whatsappUrl)
<li>No social links added yet.</li>
<li>Henüz sosyal bağlantı eklenmedi.</li>
@endif
</ul>
<h4 class="text-white font-medium mb-3">Languages</h4>
<h4 class="text-white font-medium mb-3">Diller</h4>
<div class="flex flex-wrap gap-2">
@foreach(config('app.available_locales', ['en']) as $locale)
@foreach($availableLocales as $locale)
<a href="{{ route('lang.switch', $locale) }}" class="text-xs {{ app()->getLocale() === $locale ? 'text-white' : 'hover:text-white' }} transition">{{ strtoupper($locale) }}</a>
@endforeach
</div>
</div>
</div>
<div class="border-t border-gray-700 mt-8 pt-8 text-center text-sm">
<div class="border-t border-slate-700 mt-8 pt-8 text-center text-sm text-slate-400">
<p>© {{ date('Y') }} {{ $siteName }}. All rights reserved.</p>
</div>
</div>

View File

@ -23,4 +23,7 @@ Route::get('/partner', fn () => $redirectToPartner('filament.partner.pages.dashb
Route::get('/partner/listings', fn () => $redirectToPartner('filament.partner.resources.listings.index'))
->name('partner.listings.index');
Route::get('/partner/listings/create', fn () => $redirectToPartner('filament.partner.resources.listings.create'))
->name('partner.listings.create');
require __DIR__.'/auth.php';