feat: implement full-stack awards module with Filament management, database support, and localization.

This commit is contained in:
Ümit Tunç
2026-05-27 06:32:31 +03:00
parent 05e8059b5d
commit 3c92a81cfe
14 changed files with 1127 additions and 0 deletions
@@ -0,0 +1,77 @@
<?php
namespace App\Filament\Admin\Resources\Awards;
use App\Filament\Admin\Resources\Awards\Pages\CreateAward;
use App\Filament\Admin\Resources\Awards\Pages\EditAward;
use App\Filament\Admin\Resources\Awards\Pages\ListAwards;
use App\Filament\Admin\Resources\Awards\Schemas\AwardForm;
use App\Filament\Admin\Resources\Awards\Tables\AwardsTable;
use App\Models\Award;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class AwardResource extends Resource
{
protected static ?string $model = Award::class;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-trophy';
protected static ?int $navigationSort = 46;
public static function getNavigationGroup(): ?string
{
return __('awards.navigation_group');
}
public static function getNavigationLabel(): string
{
return __('awards.navigation_label');
}
public static function getModelLabel(): string
{
return __('awards.model_label');
}
public static function getPluralModelLabel(): string
{
return __('awards.plural_model_label');
}
public static function form(Schema $schema): Schema
{
return AwardForm::configure($schema);
}
public static function table(Table $table): Table
{
return AwardsTable::configure($table);
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => ListAwards::route('/'),
'create' => CreateAward::route('/create'),
'edit' => EditAward::route('/{record}/edit'),
];
}
public static function getRecordRouteBindingEloquentQuery(): Builder
{
return parent::getRecordRouteBindingEloquentQuery()
->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Filament\Admin\Resources\Awards\Pages;
use App\Filament\Admin\Resources\Awards\AwardResource;
use App\Filament\Admin\Resources\Components\TranslationTabs;
use Filament\Resources\Pages\CreateRecord;
class CreateAward extends CreateRecord
{
protected static string $resource = AwardResource::class;
public function getTitle(): string
{
return __('awards.create') ?? 'Yeni Ödül Ekle';
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getCreatedNotificationTitle(): ?string
{
return __('awards.created_successfully') ?? 'Ödül başarıyla eklendi.';
}
protected function afterCreate(): void
{
TranslationTabs::saveTranslations($this->record, $this->form->getState());
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Filament\Admin\Resources\Awards\Pages;
use App\Filament\Admin\Resources\Awards\AwardResource;
use App\Filament\Admin\Resources\Components\TranslationTabs;
use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction;
use Filament\Resources\Pages\EditRecord;
class EditAward extends EditRecord
{
protected static string $resource = AwardResource::class;
public function getTitle(): string
{
return __('awards.edit') ?? 'Ödülü Düzenle';
}
protected function getHeaderActions(): array
{
return [
DeleteAction::make()
->label(__('awards.delete') ?? 'Sil'),
ForceDeleteAction::make()
->label(__('awards.force_delete') ?? 'Kalıcı Olarak Sil'),
RestoreAction::make()
->label(__('awards.restore') ?? 'Geri Yükle'),
];
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getSavedNotificationTitle(): ?string
{
return __('awards.updated_successfully') ?? 'Ödül başarıyla güncellendi.';
}
protected function mutateFormDataBeforeFill(array $data): array
{
return array_merge($data, TranslationTabs::fillFromRecord($this->record));
}
protected function afterSave(): void
{
TranslationTabs::saveTranslations($this->record, $this->form->getState());
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Filament\Admin\Resources\Awards\Pages;
use App\Filament\Admin\Resources\Awards\AwardResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListAwards extends ListRecords
{
protected static string $resource = AwardResource::class;
public function getTitle(): string
{
return __('awards.title') ?? 'Ödüllerimiz';
}
protected function getHeaderActions(): array
{
return [
CreateAction::make()
->label(__('awards.create') ?? 'Yeni Ödül Ekle'),
];
}
}
@@ -0,0 +1,117 @@
<?php
namespace App\Filament\Admin\Resources\Awards\Schemas;
use App\Filament\Admin\Resources\Components\TranslationTabs;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class AwardForm
{
public static function categoryOptions(): array
{
return [
'hackathon' => __('awards.category_hackathon') ?? 'Hackathon',
'export' => __('awards.category_export') ?? 'İhracat',
'innovation' => __('awards.category_innovation') ?? 'İnovasyon',
'design' => __('awards.category_design') ?? 'Tasarım',
'general' => __('awards.category_general') ?? 'Genel',
];
}
public static function configure(Schema $schema): Schema
{
return $schema
->columns(3)
->schema([
Section::make(__('awards.content_section') ?? 'Ödül İçeriği')
->schema([
TextInput::make('title')
->label(__('awards.title_field') ?? 'Ödül Başlığı (Varsayılan)')
->required()
->maxLength(255),
TextInput::make('issuer')
->label(__('awards.issuer_field') ?? 'Ödülü Veren Kurum (Varsayılan)')
->required()
->maxLength(255),
Textarea::make('description')
->label(__('awards.description_field') ?? 'Açıklama (Varsayılan)')
->required()
->rows(5)
->columnSpanFull(),
TranslationTabs::make([
'title' => [
'type' => 'text',
'label' => __('awards.title_field') ?? 'Ödül Başlığı',
'required' => false,
'maxLength' => 255,
],
'issuer' => [
'type' => 'text',
'label' => __('awards.issuer_field') ?? 'Ödülü Veren Kurum',
'required' => false,
'maxLength' => 255,
],
'description' => [
'type' => 'textarea',
'label' => __('awards.description_field') ?? 'Açıklama',
'required' => false,
'rows' => 5,
],
]),
])
->columnSpan(2),
Section::make(__('awards.settings_section') ?? 'Ödül Ayarları')
->schema([
FileUpload::make('image')
->label(__('awards.image_field') ?? 'Ödül Görseli / Logo')
->image()
->disk('public')
->directory('awards')
->required(),
DatePicker::make('award_date')
->label(__('awards.date_field') ?? 'Ödül Tarihi')
->required(),
Select::make('category')
->label(__('awards.category_field') ?? 'Kategori')
->options(self::categoryOptions())
->default('general')
->required()
->native(false),
TextInput::make('external_link')
->label(__('awards.link_field') ?? 'Doğrulama / Haber Linki')
->url()
->maxLength(255),
TextInput::make('sort_order')
->label(__('awards.sort_order_field') ?? 'Sıralama')
->numeric()
->default(0)
->minValue(0),
Toggle::make('is_featured')
->label(__('awards.is_featured_field') ?? 'Öne Çıkarılan Ödül')
->default(false),
Toggle::make('is_active')
->label(__('awards.is_active_field') ?? 'Aktif / Görünür')
->default(true),
])
->columnSpan(1),
]);
}
}
@@ -0,0 +1,100 @@
<?php
namespace App\Filament\Admin\Resources\Awards\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
class AwardsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
ImageColumn::make('image')
->label(__('awards.table_image') ?? 'Logo')
->disk('public')
->square()
->size(50),
TextColumn::make('title')
->label(__('awards.table_title') ?? 'Ödül Başlığı')
->searchable()
->sortable()
->limit(40),
TextColumn::make('issuer')
->label(__('awards.table_issuer') ?? 'Veren Kurum')
->searchable()
->sortable()
->limit(30),
TextColumn::make('award_date')
->label(__('awards.table_date') ?? 'Ödül Tarihi')
->date('d.m.Y')
->sortable()
->alignCenter(),
TextColumn::make('category')
->label(__('awards.table_category') ?? 'Kategori')
->badge()
->formatStateUsing(fn (string $state): string => match ($state) {
'hackathon' => __('awards.category_hackathon') ?? 'Hackathon',
'export' => __('awards.category_export') ?? 'İhracat',
'innovation' => __('awards.category_innovation') ?? 'İnovasyon',
'design' => __('awards.category_design') ?? 'Tasarım',
default => __('awards.category_general') ?? 'Genel',
})
->sortable()
->alignCenter(),
ToggleColumn::make('is_featured')
->label(__('awards.table_is_featured') ?? 'Öne Çıkan')
->alignCenter(),
ToggleColumn::make('is_active')
->label(__('awards.table_is_active') ?? 'Aktif')
->alignCenter(),
TextColumn::make('sort_order')
->label(__('awards.sort_order_field') ?? 'Sıra')
->sortable()
->alignCenter(),
])
->filters([
TernaryFilter::make('is_active')
->label(__('awards.is_active_field') ?? 'Aktiflik Durumu'),
TernaryFilter::make('is_featured')
->label(__('awards.is_featured_field') ?? 'Öne Çıkarılma Durumu'),
TrashedFilter::make(),
])
->recordActions([
EditAction::make()
->label(__('awards.edit') ?? 'Düzenle'),
])
->actions([
// Individual actions can be put here if necessary
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make()
->label(__('awards.delete') ?? 'Sil'),
RestoreBulkAction::make()
->label(__('awards.restore') ?? 'Geri Yükle'),
ForceDeleteBulkAction::make()
->label(__('awards.force_delete') ?? 'Kalıcı Olarak Sil'),
]),
])
->defaultSort('sort_order', 'asc')
->reorderable('sort_order');
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Models;
use App\Traits\HasTranslations;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Award extends Model
{
use HasFactory, SoftDeletes, HasTranslations;
protected $fillable = [
'title',
'description',
'issuer',
'award_date',
'image',
'external_link',
'is_featured',
'is_active',
'sort_order',
'category',
];
/**
* @var list<string>
*/
protected $translatable = [
'title',
'description',
'issuer',
];
protected $casts = [
'award_date' => 'date',
'is_featured' => 'boolean',
'is_active' => 'boolean',
'sort_order' => 'integer',
];
public function scopeActive($query)
{
return $query->where('is_active', true);
}
public function scopeFeatured($query)
{
return $query->where('is_featured', true);
}
public function scopeOrdered($query)
{
return $query
->orderBy('sort_order', 'asc')
->orderBy('award_date', 'desc');
}
}
@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('awards', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable(); // Original/fallback title
$table->text('description')->nullable(); // Original/fallback description
$table->string('issuer')->nullable(); // Original/fallback issuer
$table->date('award_date')->nullable();
$table->string('image')->nullable();
$table->string('external_link')->nullable();
$table->boolean('is_featured')->default(false);
$table->boolean('is_active')->default(true);
$table->unsignedInteger('sort_order')->default(0);
$table->string('category', 64)->default('general');
$table->timestamps();
$table->softDeletes();
$table->index(['is_active', 'sort_order']);
$table->index('is_featured');
$table->index('award_date');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('awards');
}
};
+100
View File
@@ -0,0 +1,100 @@
<?php
namespace Database\Seeders;
use App\Models\Award;
use Illuminate\Database\Seeder;
class AwardSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$awards = [
[
'title' => 'Here & AWS & THY Hackathon Birinciliği',
'description' => '21 Mart 2019 yılında Here, Amazon Web Services (AWS) ve Türk Hava Yolları\'nın (THY) ortaklaşa yürüttüğü Hackathon kapsamında büyük bir başarı göstererek 1. lik ödülünü almaya hak kazandık.',
'issuer' => 'Here & AWS & THY',
'award_date' => '2019-03-21',
'image' => 'assets/img/awards/truncgil-hackathon-birincisi-here-aws-thy.png',
'external_link' => 'https://truncgil.com',
'is_featured' => true,
'is_active' => true,
'sort_order' => 1,
'category' => 'hackathon',
'translations' => [
'en' => [
'title' => 'Here & AWS & THY Hackathon First Place',
'description' => 'On March 21, 2019, we achieved outstanding success and won the 1st place award in the Hackathon jointly organized by Here, Amazon Web Services (AWS), and Turkish Airlines (THY).',
'issuer' => 'Here, AWS & Turkish Airlines',
]
],
],
[
'title' => 'Gaziantep Teknopark Yılın İhracat Ödülü',
'description' => 'İhracat faaliyetlerindeki üstün başarımızdan ötürü 26 Aralık 2019 yılında Gaziantep Teknopark tarafından "Yılın İhracat Ödülü"ne layık görüldük.',
'issuer' => 'Gaziantep Teknopark',
'award_date' => '2019-12-26',
'image' => 'assets/img/awards/truncgil-teknoloji-yilin-ihracat-odulu.png',
'external_link' => 'https://truncgil.com',
'is_featured' => true,
'is_active' => true,
'sort_order' => 2,
'category' => 'export',
'translations' => [
'en' => [
'title' => 'Gaziantep Technopark Export of the Year Award',
'description' => 'Due to our outstanding achievements in export activities, we were deemed worthy of the "Export of the Year Award" by Gaziantep Technopark on December 26, 2019.',
'issuer' => 'Gaziantep Technopark',
]
],
],
[
'title' => 'HackSmarCity Hackathon Birinciliği',
'description' => 'Gaziantep Teknopark, Target Teknoloji Transfer Ofisi, Gaziantep Büyükşehir Belediyesi ve Gaziantep Bilişim\'in ortaklaşa düzenlediği akıllı şehir çözümleri temalı HackSmarCity Hackathon\'unda 1. lik elde ettik.',
'issuer' => 'Gaziantep Büyükşehir Belediyesi & Teknopark',
'award_date' => '2019-11-15', // Approximate date in 2019/2020
'image' => 'assets/img/awards/truncgil-teknoloji-hacksmarcity.jpg',
'external_link' => 'https://truncgil.com',
'is_featured' => false,
'is_active' => true,
'sort_order' => 3,
'category' => 'hackathon',
'translations' => [
'en' => [
'title' => 'HackSmarCity Hackathon First Place',
'description' => 'We achieved 1st place in the HackSmarCity Hackathon themed around smart city solutions, jointly organized by Gaziantep Technopark, Target Technology Transfer Office, Gaziantep Metropolitan Municipality, and Gaziantep Bilisim.',
'issuer' => 'Gaziantep Metropolitan Municipality & Technopark',
]
],
],
];
foreach ($awards as $awardData) {
$translations = $awardData['translations'] ?? [];
unset($awardData['translations']);
$award = Award::updateOrCreate(
[
'award_date' => $awardData['award_date'],
'image' => $awardData['image'],
],
$awardData
);
// Save default (Turkish) translations in translation table too so HasTranslations resolves correctly
$award->setTranslation('title', 'tr', $awardData['title'], 'published');
$award->setTranslation('description', 'tr', $awardData['description'], 'published');
$award->setTranslation('issuer', 'tr', $awardData['issuer'], 'published');
// Save English translations
foreach ($translations as $langCode => $fields) {
foreach ($fields as $fieldName => $value) {
$award->setTranslation($fieldName, $langCode, $value, 'published');
}
}
}
}
}
+1
View File
@@ -27,6 +27,7 @@ class DatabaseSeeder extends Seeder
DekupaiProductSeeder::class,
FinanceProductSeeder::class,
BlogSeeder::class,
AwardSeeder::class,
]);
// User::factory(10)->create();
+18
View File
@@ -78,6 +78,24 @@ class PageSeeder extends Seeder
]
);
// 2.1.2 Ödüllerimiz (Child of Kurumsal)
Page::updateOrCreate(
['slug' => 'odullerimiz'],
[
'author_id' => $author->id,
'parent_id' => $corporate->id,
'title' => 'Ödüllerimiz',
'excerpt' => 'Yenilikçi teknolojilerimizle sektöre yön veriyor, başarılarimizi küresel ödüllerle taçlandırıyoruz.',
'content' => '<p>Yenilikçi teknolojilerimizle sektöre yön veriyor, başarılarimizi küresel ödüllerle taçlandırıyoruz.</p>',
'template' => 'odullerimiz',
'status' => 'published',
'is_homepage' => false,
'show_in_menu' => true,
'sort_order' => 6, // Sort order among Kurumsal children
'published_at' => now(),
]
);
// 2.2 Vizyon & Misyon (Child of Kurumsal)
Page::updateOrCreate(
['slug' => 'vizyon-misyon'],
+47
View File
@@ -0,0 +1,47 @@
<?php
return [
'navigation_group' => 'Corporate',
'title' => 'Our Awards',
'navigation_label' => 'Our Awards',
'model_label' => 'Award',
'plural_model_label' => 'Our Awards',
'create' => 'Add New Award',
'edit' => 'Edit Award',
'delete' => 'Delete',
'restore' => 'Restore',
'force_delete' => 'Force Delete',
'content_section' => 'Award Details',
'settings_section' => 'Award Settings',
'title_field' => 'Award Title',
'issuer_field' => 'Issuing Organization',
'description_field' => 'Description',
'image_field' => 'Award Logo / Badge',
'date_field' => 'Award Date',
'category_field' => 'Category',
'link_field' => 'News / Verification Link',
'sort_order_field' => 'Sort Order',
'is_featured_field' => 'Feature Award',
'is_active_field' => 'Active / Visible',
'table_image' => 'Logo',
'table_title' => 'Award Title',
'table_issuer' => 'Issuer',
'table_date' => 'Date',
'table_category' => 'Category',
'table_is_featured' => 'Featured',
'table_is_active' => 'Active',
'created_successfully' => 'Award successfully created.',
'updated_successfully' => 'Award successfully updated.',
'deleted_successfully' => 'Award successfully deleted.',
'category_hackathon' => 'Hackathon / Competition',
'category_export' => 'Export / Success',
'category_innovation' => 'Innovation / R&D',
'category_design' => 'Design / UI',
'category_general' => 'General Award',
];
+47
View File
@@ -0,0 +1,47 @@
<?php
return [
'navigation_group' => 'Kurumsal',
'title' => 'Ödüllerimiz',
'navigation_label' => 'Ödüllerimiz',
'model_label' => 'Ödül',
'plural_model_label' => 'Ödüllerimiz',
'create' => 'Yeni Ödül Ekle',
'edit' => 'Ödülü Düzenle',
'delete' => 'Sil',
'restore' => 'Geri Yükle',
'force_delete' => 'Kalıcı Olarak Sil',
'content_section' => 'Ödül Bilgileri',
'settings_section' => 'Ödül Ayarları',
'title_field' => 'Ödül Adı',
'issuer_field' => 'Ödülü Veren Kurum',
'description_field' => 'Açıklama',
'image_field' => 'Ödül Görseli / Logo',
'date_field' => 'Ödül Tarihi',
'category_field' => 'Kategori',
'link_field' => 'Haber / Doğrulama Linki',
'sort_order_field' => 'Sıralama',
'is_featured_field' => 'Öne Çıkar',
'is_active_field' => 'Aktif / Görünür',
'table_image' => 'Logo',
'table_title' => 'Ödül Adı',
'table_issuer' => 'Veren Kurum',
'table_date' => 'Tarih',
'table_category' => 'Kategori',
'table_is_featured' => 'Öne Çıkan',
'table_is_active' => 'Aktif',
'created_successfully' => 'Ödül başarıyla eklendi.',
'updated_successfully' => 'Ödül başarıyla güncellendi.',
'deleted_successfully' => 'Ödül başarıyla silindi.',
'category_hackathon' => 'Hackathon / Yarışma',
'category_export' => 'İhracat / Başarı',
'category_innovation' => 'İnovasyon / Ar-Ge',
'category_design' => 'Tasarım / Arayüz',
'category_general' => 'Genel Ödül',
];
@@ -0,0 +1,410 @@
@extends('layouts.site', [
'header' => 'partials.header-demo1',
'footer' => 'partials.footer',
])
@section('content')
@php
$awards = \App\Models\Award::active()->ordered()->get();
// Extracted categories from active awards for dynamic filter options
$categories = $awards->pluck('category')->unique()->toArray();
$heroTitle = $page ? ($page->translate('title') ?: $page->title) : t('Ödüllerimiz ve Başarılarımız');
$heroExcerpt = $page ? ($page->translate('excerpt') ?: $page->excerpt) : t('Yenilikçi teknolojilerimizle sektöre yön veriyor, başarılarımızı küresel ödüllerle taçlandırıyoruz.');
// Timeline color palette
$colors = ['#e31e24', '#f5a623', '#27ae60', '#2980b9', '#8e44ad', '#1abc9c'];
@endphp
<div class="grow shrink-0 bg-[#fbfcfd]">
<!-- Hero Section -->
<section class="wrapper image-wrapper bg-image bg-overlay bg-overlay-400 !text-white bg-no-repeat bg-[center_center] bg-cover relative z-0 !bg-fixed before:content-[''] before:block before:absolute before:z-[1] before:w-full before:h-full before:left-0 before:top-0 before:bg-[rgba(30,34,40,.55)]" data-image-src="{{ asset('assets/img/photos/bg4.webp') }}" style="background-image: url('{{ asset('assets/img/photos/bg4.webp') }}');">
<div class="container pt-28 pb-40 xl:pt-36 lg:pt-36 md:pt-36 xl:pb-[12.5rem] lg:pb-[12.5rem] md:pb-[12.5rem] !text-center">
<div class="flex flex-wrap mx-[-15px]">
<div class="xl:w-8/12 lg:w-8/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto">
<h1 class="!text-[calc(1.365rem_+_1.38vw)] font-bold !leading-[1.2] xl:!text-[2.6rem] !mb-4 !text-white">{{ $heroTitle }}</h1>
<p class="lead !text-[1.05rem] !leading-[1.6] !text-white opacity-90 max-w-2xl mx-auto mb-6">{{ $heroExcerpt }}</p>
<nav class="inline-block" aria-label="breadcrumb">
<ol class="breadcrumb flex flex-wrap bg-[none] p-0 !rounded-none list-none !mb-[20px] justify-center">
<li class="breadcrumb-item flex !text-[#c3c3c3]"><a class="!text-white hover:text-[#e31e24]" href="{{ url('/') }}">{{ t('Anasayfa') }}</a></li>
<li class="breadcrumb-item flex !text-white !pl-2 before:font-normal before:!flex before:items-center before:text-[rgba(255,255,255,.5)] before:content-['\e931'] before:text-[0.9rem] before:-mt-px before:!pr-2 before:font-Unicons active" aria-current="page">{{ t('Ödüllerimiz') }}</li>
</ol>
</nav>
</div>
</div>
</div>
</section>
<!-- Main Content wrapper with Timeline -->
<section class="wrapper !bg-[#f4f6f8] relative border-0 upper-end before:top-[-4rem] before:border-l-transparent before:border-r-[100vw] before:border-t-[4rem] before:border-[#f4f6f8] before:content-[''] before:block before:absolute before:z-0 before:border-y-transparent before:border-0 before:border-solid before:right-0">
<div class="container pb-16">
<div class="xl:w-10/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto !mt-[-9rem] relative z-10">
<!-- Category Filters -->
<div class="flex flex-wrap justify-center gap-2 mb-14" id="awards-filter-tabs">
<button class="filter-btn active px-5 py-2 text-xs font-bold rounded-full transition-all border bg-white text-gray-700 hover:bg-gray-100 border-gray-200" data-filter="all">
{{ t('Tüm Ödüller') }}
</button>
@if(in_array('hackathon', $categories))
<button class="filter-btn px-5 py-2 text-xs font-bold rounded-full transition-all border bg-white text-gray-700 hover:bg-gray-100 border-gray-200" data-filter="hackathon">
{{ t('Hackathon / Yarışma') }}
</button>
@endif
@if(in_array('export', $categories))
<button class="filter-btn px-5 py-2 text-xs font-bold rounded-full transition-all border bg-white text-gray-700 hover:bg-gray-100 border-gray-200" data-filter="export">
{{ t('İhracat / Başarı') }}
</button>
@endif
@if(in_array('innovation', $categories))
<button class="filter-btn px-5 py-2 text-xs font-bold rounded-full transition-all border bg-white text-gray-700 hover:bg-gray-100 border-gray-200" data-filter="innovation">
{{ t('İnovasyon / Ar-Ge') }}
</button>
@endif
@if(in_array('design', $categories))
<button class="filter-btn px-5 py-2 text-xs font-bold rounded-full transition-all border bg-white text-gray-700 hover:bg-gray-100 border-gray-200" data-filter="design">
{{ t('Tasarım') }}
</button>
@endif
</div>
<!-- Timeline Container -->
<div class="awards-timeline" data-timeline-animate>
<div class="awards-timeline__start awards-timeline__reveal">
<span>{{ t('BAŞLANGIÇ') }}</span>
</div>
<div class="awards-timeline__track">
@foreach($awards as $index => $award)
@php
$color = $colors[$index % count($colors)];
$position = $index % 2 === 0 ? 'right' : 'left';
$imgSrc = str_starts_with($award->image, 'assets/') ? asset($award->image) : asset('storage/' . $award->image);
@endphp
<article class="awards-timeline__item awards-timeline__item--{{ $position }} awards-timeline__reveal" style="--item-color: {{ $color }};" data-category="{{ $award->category }}">
<!-- Timeline Axis Dot & Line -->
<div class="awards-timeline__axis">
<span class="awards-timeline__dot"></span>
</div>
<!-- Timeline Card -->
<div class="awards-timeline__side awards-timeline__side--inner">
<div class="awards-timeline__content bg-white rounded-[0.8rem] border border-[rgba(30,34,40,0.06)] hover:translate-y-[-0.25rem] transition-all duration-300 hover:shadow-[0_1rem_2.5rem_rgba(30,34,40,0.08)] flex flex-col overflow-hidden group">
<!-- Ceremony Photo Banner -->
<div class="award-photo-wrapper" style="width: 100%; height: 240px; overflow: hidden; position: relative; background: #eef1f4;">
<img src="{{ $imgSrc }}" alt="{{ $award->translate('title') }}" style="width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease;" class="award-photo-img group-hover:scale-105" />
<!-- Badge on Image -->
<div style="position: absolute; top: 12px; left: 12px; display: flex; gap: 6px;">
<span class="badge bg-[#1e2229]/80 !text-white text-[0.62rem] uppercase tracking-wider px-2.5 py-1" style="border-radius: 4px;">
{{ $award->category === 'hackathon' ? t('Yarışma') : ($award->category === 'export' ? t('İhracat') : t('Başarı')) }}
</span>
@if($award->is_featured)
<span class="badge bg-[#f5a623] !text-white text-[0.62rem] uppercase tracking-wider px-2.5 py-1" style="border-radius: 4px;">
<i class="uil uil-star"></i> {{ t('Öne Çıkan') }}
</span>
@endif
</div>
</div>
<!-- Card Info Padding Content -->
<div class="p-6 md:p-8 flex flex-col gap-3 text-start">
<div style="font-size: 0.72rem; color: #959ca9; font-weight: 600;">
<i class="uil uil-calendar-alt"></i> {{ $award->award_date ? $award->award_date->translatedFormat('d F Y') : '' }}
</div>
<h3 class="text-base font-extrabold text-gray-900 mb-1 leading-snug group-hover:text-[#e31e24] transition-colors duration-300">
{{ $award->translate('title') }}
</h3>
<p class="text-xs leading-relaxed text-gray-500 mb-2">
{{ $award->translate('description') }}
</p>
<!-- Footer elements -->
<div class="flex items-center justify-between pt-3 border-t border-gray-100 mt-2">
<span class="text-[0.72rem] font-bold text-gray-600 flex items-center gap-1">
<i class="uil uil-award text-[#e31e24] text-sm"></i> {{ $award->translate('issuer') }}
</span>
@if($award->external_link)
<a href="{{ $award->external_link }}" target="_blank" class="text-[0.72rem] font-bold text-[#e31e24] hover:underline flex items-center gap-0.5">
{{ t('Doğrula') }} <i class="uil uil-external-link-alt"></i>
</a>
@endif
</div>
</div>
</div>
</div>
</article>
@endforeach
</div>
<div class="awards-timeline__finish awards-timeline__reveal">
<span>{{ t('BUGÜN') }}</span>
</div>
</div>
<!-- Standards and Trust elements -->
<div class="my-16 border-t border-gray-200/60 pt-16">
<div class="text-center mb-8">
<span class="badge bg-pale-red text-red !rounded-[50rem] uppercase tracking-wider font-semibold text-[0.7rem] px-3 py-1 mb-2 inline-block">{{ t('GÜVEN & STANDARTLAR') }}</span>
<h3 class="!text-[calc(1.2rem_+_0.6vw)] font-bold xl:!text-[1.8rem] !leading-[1.3] text-[#1e2229]">{{ t('Standartlarımız ve Sertifikalarımız') }}</h3>
<p class="text-xs text-gray-500 leading-relaxed max-w-xl mx-auto mt-2">{{ t('Bir teknoloji şirketi olarak, geliştirdiğimiz tüm sistemlerde ulusal ve uluslararası güvenlik ve kalite standartlarına tam uyum sağlıyoruz.') }}</p>
</div>
<div class="flex flex-wrap justify-center items-center gap-8 md:gap-12 mt-10">
<!-- ISO 27001 -->
<div class="flex flex-col items-center max-w-[120px] text-center group">
<div class="w-16 h-16 rounded-full border border-gray-200/80 bg-white shadow-sm flex items-center justify-center p-3 mb-2 group-hover:border-red/40 group-hover:shadow-md transition-all duration-300">
<i class="uil uil-shield-check text-2xl text-gray-400 group-hover:text-red transition-colors duration-300"></i>
</div>
<span class="text-xs font-bold text-gray-700 leading-tight">ISO 27001</span>
<span class="text-[0.62rem] text-gray-400 mt-0.5">{{ t('Bilgi Güvenliği') }}</span>
</div>
<!-- ISO 9001 -->
<div class="flex flex-col items-center max-w-[120px] text-center group">
<div class="w-16 h-16 rounded-full border border-gray-200/80 bg-white shadow-sm flex items-center justify-center p-3 mb-2 group-hover:border-red/40 group-hover:shadow-md transition-all duration-300">
<i class="uil uil-check-square text-2xl text-gray-400 group-hover:text-red transition-colors duration-300"></i>
</div>
<span class="text-xs font-bold text-gray-700 leading-tight">ISO 9001</span>
<span class="text-[0.62rem] text-gray-400 mt-0.5">{{ t('Kalite Yönetimi') }}</span>
</div>
<!-- KVKK / GDPR -->
<div class="flex flex-col items-center max-w-[120px] text-center group">
<div class="w-16 h-16 rounded-full border border-gray-200/80 bg-white shadow-sm flex items-center justify-center p-3 mb-2 group-hover:border-red/40 group-hover:shadow-md transition-all duration-300">
<i class="uil uil-lock text-2xl text-gray-400 group-hover:text-red transition-colors duration-300"></i>
</div>
<span class="text-xs font-bold text-gray-700 leading-tight">KVKK / GDPR</span>
<span class="text-[0.62rem] text-gray-400 mt-0.5">{{ t('Veri Güvenliği') }}</span>
</div>
<!-- Teknopark -->
<div class="flex flex-col items-center max-w-[120px] text-center group">
<div class="w-16 h-16 rounded-full border border-gray-200/80 bg-white shadow-sm flex items-center justify-center p-3 mb-2 group-hover:border-red/40 group-hover:shadow-md transition-all duration-300">
<i class="uil uil-building text-2xl text-gray-400 group-hover:text-red transition-colors duration-300"></i>
</div>
<span class="text-xs font-bold text-gray-700 leading-tight">Teknopark</span>
<span class="text-[0.62rem] text-gray-400 mt-0.5">{{ t('Ar-Ge Liderliği') }}</span>
</div>
</div>
</div>
<!-- Employer Branding Closing CTA -->
<div class="my-16 bg-gradient-to-r from-red to-rose-600 rounded-[1rem] p-8 md:p-12 !text-white text-center shadow-lg relative overflow-hidden group">
<div class="absolute -right-16 -top-16 w-48 h-48 rounded-full bg-white/10 opacity-30 group-hover:scale-110 transition-transform duration-500"></div>
<div class="absolute -left-16 -bottom-16 w-48 h-48 rounded-full bg-white/10 opacity-30 group-hover:scale-110 transition-transform duration-500"></div>
<h3 class="text-xl md:text-2xl font-bold !text-white mb-3 relative z-10">{{ t('Bu başarıları birlikte kazandık!') }}</h3>
<p class="text-sm leading-relaxed max-w-xl mx-auto mb-6 opacity-90 relative z-10">{{ t('Geleceği yenilikçi teknolojilerle şekillendiren bu harika ekibin bir parçası olmak, bizimle yeni ödüllere koşmak ister misin?') }}</p>
<a href="{{ url('/kariyer') }}" class="btn btn-white !rounded-[50rem] px-6 py-2.5 text-xs font-bold hover:translate-y-[-0.15rem] hover:shadow-md transition-all relative z-10 !bg-white !text-[#e31e24] hover:!bg-gray-50 border-0">
{{ t('Aramıza Katıl') }} <i class="uil uil-arrow-right ml-1"></i>
</a>
</div>
</div>
</div>
</section>
</div>
@push('styles')
<style>
.filter-btn.active {
background-color: #e31e24 !important;
border-color: #e31e24 !important;
color: #ffffff !important;
box-shadow: 0 4px 12px rgba(227, 30, 36, 0.2);
}
/* Timeline Design System drawing inspiration from hakkimizda */
.awards-timeline {
max-width: 960px;
margin: 0 auto;
position: relative;
}
.awards-timeline__start,
.awards-timeline__finish {
display: flex;
justify-content: center;
margin-bottom: 2rem;
}
.awards-timeline__finish {
margin-top: 2rem;
margin-bottom: 0;
}
.awards-timeline__start span,
.awards-timeline__finish span {
display: inline-block;
padding: 0.45rem 1.4rem;
border: 2px solid #cbd5e0;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.16em;
color: #4a5568;
background: #fff;
}
.awards-timeline__track {
position: relative;
padding: 0.5rem 0;
}
.awards-timeline__track::before {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 4px;
transform: translateX(-50%);
background: linear-gradient(to bottom, #e31e24, #f5a623, #27ae60, #2980b9);
border-radius: 4px;
z-index: 0;
}
.awards-timeline__item {
position: relative;
display: grid;
grid-template-columns: 1fr 40px 1fr;
align-items: center;
gap: 0 2rem;
margin-bottom: 2.75rem;
z-index: 1;
transition: all 0.45s ease;
}
.awards-timeline__item:last-child {
margin-bottom: 0;
}
.awards-timeline__axis {
grid-column: 2;
display: flex;
align-items: center;
justify-content: center;
}
.awards-timeline__dot {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--item-color);
border: 3px solid #fff;
box-shadow: 0 0 0 2px var(--item-color);
transition: all 0.3s ease;
}
.awards-timeline__item:hover .awards-timeline__dot {
transform: scale(1.3);
}
/* Alternating Right side */
.awards-timeline__item--right .awards-timeline__side--inner {
grid-column: 3;
text-align: left;
}
/* Alternating Left side */
.awards-timeline__item--left .awards-timeline__side--inner {
grid-column: 1;
text-align: right;
}
/* Animations reveal */
.awards-timeline__reveal {
opacity: 0;
transform: translate3d(0, 32px, 0);
transition: opacity 0.85s cubic-bezier(0.22, 1, 0.36, 1), transform 0.85s cubic-bezier(0.22, 1, 0.36, 1);
will-change: opacity, transform;
}
.awards-timeline__reveal.is-visible {
opacity: 1;
transform: translate3d(0, 0, 0);
}
/* Dynamic hide class */
.awards-timeline__item.hidden-by-filter {
display: none !important;
}
@media (max-width: 767px) {
.awards-timeline__track::before {
left: 20px;
transform: none;
}
.awards-timeline__item {
grid-template-columns: 40px 1fr;
gap: 0 1rem;
}
.awards-timeline__axis {
grid-column: 1;
align-self: start;
padding-top: 1.5rem;
}
.awards-timeline__item--left .awards-timeline__side--inner,
.awards-timeline__item--right .awards-timeline__side--inner {
grid-column: 2;
text-align: left;
}
}
</style>
@endpush
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const filterButtons = document.querySelectorAll('.filter-btn');
const awardCards = document.querySelectorAll('.awards-timeline__item');
filterButtons.forEach(button => {
button.addEventListener('click', function() {
filterButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
const filterValue = this.getAttribute('data-filter');
awardCards.forEach(card => {
if (filterValue === 'all' || card.getAttribute('data-category') === filterValue) {
card.classList.remove('hidden-by-filter');
} else {
card.classList.add('hidden-by-filter');
}
});
});
});
// Reveal animation on scroll drawing inspiration from hakkimizda.blade.php
const revealElements = document.querySelectorAll('.awards-timeline__reveal');
if (revealElements.length > 0) {
const observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
});
}, {
root: null,
rootMargin: '0px 0px -10% 0px',
threshold: 0.1,
});
revealElements.forEach(function (element) {
observer.observe(element);
});
}
});
</script>
@endpush
@endsection