feat: add PartnersPageSeeder for populating partner data and enhance partners view with improved layout and styling for better user experience
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class PartnersPageSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* @var array<int, array{name: string, url: string, slug: string, simple_icon?: string}>
|
||||
*/
|
||||
protected array $partners = [
|
||||
// Türkiye — domain, hosting ve dijital altyapı
|
||||
['name' => 'Turhost', 'url' => 'https://www.turhost.com', 'slug' => 'turhost'],
|
||||
['name' => 'Natro', 'url' => 'https://www.natro.com', 'slug' => 'natro'],
|
||||
['name' => 'İsimtescil', 'url' => 'https://www.isimtescil.net', 'slug' => 'isimtescil'],
|
||||
['name' => 'Radore', 'url' => 'https://www.radore.com', 'slug' => 'radore'],
|
||||
['name' => 'Nic.tr', 'url' => 'https://www.nic.tr', 'slug' => 'nic-tr'],
|
||||
['name' => 'Türk Telekom', 'url' => 'https://www.turktelekom.com.tr', 'slug' => 'turk-telekom'],
|
||||
['name' => 'Bulutistan', 'url' => 'https://www.bulutistan.com', 'slug' => 'bulutistan'],
|
||||
['name' => 'Güzel Hosting', 'url' => 'https://www.guzel.net.tr', 'slug' => 'guzel-hosting'],
|
||||
['name' => 'DorukNet', 'url' => 'https://www.doruk.net', 'slug' => 'doruknet'],
|
||||
|
||||
// Global teknoloji ortakları
|
||||
['name' => 'Google', 'url' => 'https://www.google.com', 'slug' => 'google', 'simple_icon' => 'google'],
|
||||
['name' => 'Google Gemini', 'url' => 'https://gemini.google.com', 'slug' => 'google-gemini', 'simple_icon' => 'googlegemini'],
|
||||
['name' => 'Apple', 'url' => 'https://www.apple.com', 'slug' => 'apple', 'simple_icon' => 'apple'],
|
||||
['name' => 'Meta', 'url' => 'https://www.meta.com', 'slug' => 'meta', 'simple_icon' => 'meta'],
|
||||
['name' => 'Cursor', 'url' => 'https://www.cursor.com', 'slug' => 'cursor'],
|
||||
['name' => 'Microsoft Azure', 'url' => 'https://azure.microsoft.com', 'slug' => 'microsoft-azure', 'simple_icon' => 'microsoftazure'],
|
||||
['name' => 'Cloudflare', 'url' => 'https://www.cloudflare.com', 'slug' => 'cloudflare', 'simple_icon' => 'cloudflare'],
|
||||
];
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$author = User::query()->first();
|
||||
if (!$author) {
|
||||
$this->command?->warn('Kullanıcı bulunamadı, seeder atlandı.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$corporate = Page::query()->where('slug', '=', 'kurumsal')->first();
|
||||
|
||||
Storage::disk('public')->makeDirectory('partners', 0755, true);
|
||||
|
||||
$partnerRecords = [];
|
||||
|
||||
foreach ($this->partners as $partner) {
|
||||
$logoPath = $this->downloadLogo($partner);
|
||||
|
||||
if (!$logoPath) {
|
||||
$this->command?->warn("Logo indirilemedi: {$partner['name']}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$partnerRecords[] = [
|
||||
'name' => $partner['name'],
|
||||
'url' => $partner['url'],
|
||||
'logo' => $logoPath,
|
||||
];
|
||||
|
||||
$this->command?->info("✓ {$partner['name']}");
|
||||
}
|
||||
|
||||
Page::updateOrCreate(
|
||||
['slug' => 'cozum-ortaklari'],
|
||||
[
|
||||
'author_id' => $author->id,
|
||||
'parent_id' => $corporate?->id,
|
||||
'title' => 'Çözüm Ortakları',
|
||||
'excerpt' => 'Türkiye\'nin önde gelen domain, hosting ve dijital altyapı sağlayıcıları ile Google, Apple, Meta, Cursor ve Gemini gibi global teknoloji liderleriyle güçlü bir ekosistem.',
|
||||
'content' => '<p>Güçlü çözüm ortaklıklarımız sayesinde müşterilerimize daha hızlı, daha yenilikçi ve daha sürdürülebilir çözümler sunuyoruz. Alan adı ve hosting hizmetlerinden bulut altyapısına, yapay zeka platformlarından kurumsal yazılım ekosistemlerine kadar geniş bir partner ağıyla projelerinizi güvenle hayata geçiriyoruz.</p>',
|
||||
'template' => 'corporate.partners',
|
||||
'status' => 'published',
|
||||
'is_homepage' => false,
|
||||
'show_in_menu' => true,
|
||||
'sort_order' => 7,
|
||||
'published_at' => now(),
|
||||
'meta_title' => 'Çözüm Ortaklarımız | Trunçgil',
|
||||
'meta_description' => 'Domain, hosting, veri merkezi ve bulut altyapısı ortaklarımız ile Google, Apple, Meta, Cursor ve Gemini gibi global teknoloji iş birliklerimiz.',
|
||||
'data' => [
|
||||
'partners' => $partnerRecords,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->command?->info('✅ ' . count($partnerRecords) . ' çözüm ortağı veritabanına kaydedildi.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{name: string, url: string, slug: string, simple_icon?: string} $partner
|
||||
*/
|
||||
protected function downloadLogo(array $partner): ?string
|
||||
{
|
||||
$filename = 'partners/' . $partner['slug'] . '.png';
|
||||
$fullPath = Storage::disk('public')->path($filename);
|
||||
|
||||
$domain = parse_url($partner['url'], PHP_URL_HOST);
|
||||
$domain = $domain ? preg_replace('/^www\./', '', $domain) : null;
|
||||
|
||||
$sources = [];
|
||||
|
||||
if (!empty($partner['simple_icon'])) {
|
||||
$sources[] = 'https://cdn.simpleicons.org/' . $partner['simple_icon'];
|
||||
}
|
||||
|
||||
if ($domain) {
|
||||
$sources[] = 'https://logo.clearbit.com/' . $domain;
|
||||
$sources[] = 'https://www.google.com/s2/favicons?domain=' . $domain . '&sz=128';
|
||||
}
|
||||
|
||||
foreach ($sources as $source) {
|
||||
try {
|
||||
$response = Http::timeout(15)
|
||||
->withHeaders(['User-Agent' => 'Mozilla/5.0 (compatible; TruncgilSeeder/1.0)'])
|
||||
->get($source);
|
||||
|
||||
if (!$response->successful() || strlen($response->body()) < 200) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$body = $response->body();
|
||||
$image = @imagecreatefromstring($body);
|
||||
|
||||
if ($image === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$width = imagesx($image);
|
||||
$height = imagesy($image);
|
||||
|
||||
$canvas = imagecreatetruecolor(256, 100);
|
||||
imagealphablending($canvas, false);
|
||||
imagesavealpha($canvas, true);
|
||||
$transparent = imagecolorallocatealpha($canvas, 255, 255, 255, 127);
|
||||
imagefill($canvas, 0, 0, $transparent);
|
||||
imagealphablending($canvas, true);
|
||||
|
||||
$scale = min(220 / max($width, 1), 72 / max($height, 1));
|
||||
$newWidth = (int) max(1, round($width * $scale));
|
||||
$newHeight = (int) max(1, round($height * $scale));
|
||||
$offsetX = (int) ((256 - $newWidth) / 2);
|
||||
$offsetY = (int) ((100 - $newHeight) / 2);
|
||||
|
||||
imagecopyresampled($canvas, $image, $offsetX, $offsetY, 0, 0, $newWidth, $newHeight, $width, $height);
|
||||
imagepng($canvas, $fullPath, 8);
|
||||
|
||||
imagedestroy($image);
|
||||
imagedestroy($canvas);
|
||||
|
||||
return $filename;
|
||||
} catch (\Throwable) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->generatePlaceholder($partner, $fullPath) ? $filename : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{name: string, url: string, slug: string} $partner
|
||||
*/
|
||||
protected function generatePlaceholder(array $partner, string $fullPath): bool
|
||||
{
|
||||
$canvas = imagecreatetruecolor(256, 100);
|
||||
imagealphablending($canvas, false);
|
||||
imagesavealpha($canvas, true);
|
||||
|
||||
$bg = imagecolorallocatealpha($canvas, 248, 250, 252, 0);
|
||||
imagefill($canvas, 0, 0, $bg);
|
||||
|
||||
$textColor = imagecolorallocate($canvas, 30, 41, 59);
|
||||
$initials = Str::upper(Str::substr(Str::before($partner['name'], ' '), 0, 2));
|
||||
|
||||
$font = 5;
|
||||
$textWidth = imagefontwidth($font) * strlen($initials);
|
||||
$textHeight = imagefontheight($font);
|
||||
imagestring($canvas, $font, (int) ((256 - $textWidth) / 2), (int) ((100 - $textHeight) / 2), $initials, $textColor);
|
||||
|
||||
$result = imagepng($canvas, $fullPath, 8);
|
||||
imagedestroy($canvas);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -86,11 +86,53 @@
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.partners-cta-card {
|
||||
background: #ffffff;
|
||||
border-radius: 0.8rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
box-shadow: rgba(30, 34, 40, 0.02) 0 2px 1px, rgba(30, 34, 40, 0.02) 0 4px 2px, rgba(30, 34, 40, 0.02) 0 8px 4px, rgba(30, 34, 40, 0.02) 0 16px 8px, rgba(30, 34, 40, 0.03) 0 32px 16px;
|
||||
.partners-showcase {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.partners-showcase__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem 1.5rem;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.partners-showcase__grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 2.25rem 1.75rem;
|
||||
}
|
||||
}
|
||||
.partners-showcase__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
text-decoration: none;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
.partners-showcase__item:hover {
|
||||
transform: scale(1.06);
|
||||
}
|
||||
.partners-showcase__item img {
|
||||
max-width: 100%;
|
||||
max-height: 72px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
filter: grayscale(15%);
|
||||
opacity: 0.9;
|
||||
transition: filter 0.25s ease, opacity 0.25s ease;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.partners-showcase__item img {
|
||||
max-height: 88px;
|
||||
}
|
||||
}
|
||||
.partners-showcase__item:hover img {
|
||||
filter: grayscale(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
.partners-stats-badge {
|
||||
display: inline-flex;
|
||||
@@ -180,38 +222,35 @@
|
||||
<div class="absolute" style="top: 50%; left: 50%; width: 130%; height: auto; transform: translate(-50%,-50%); z-index:0; pointer-events: none;">
|
||||
<img class="w-full opacity-40" src="{{ asset('assets/img/photos/blurry.png') }}" alt="" loading="lazy">
|
||||
</div>
|
||||
<div class="flex flex-wrap mx-[-15px] xl:mx-[-12.5px] lg:mx-[-12.5px] md:mx-[-12.5px] !mt-[-25px] relative z-[1]">
|
||||
@foreach(array_slice(array_filter($partners, fn ($p) => !empty($p['logo'])), 0, 4) as $index => $featured)
|
||||
<div class="partners-showcase relative z-[1]">
|
||||
<div class="partners-showcase__grid">
|
||||
@foreach(array_slice(array_filter($partners, fn ($p) => !empty($p['logo'])), 0, 7) as $featured)
|
||||
@php
|
||||
$colClasses = match ($index) {
|
||||
0 => 'md:w-6/12 lg:w-6/12 xl:w-5/12 w-full !self-end',
|
||||
1 => 'xl:w-6/12 lg:w-6/12 md:w-6/12 w-full !self-end',
|
||||
2 => 'md:w-6/12 lg:w-6/12 xl:w-5/12 xl:!ml-[8.33333333%]',
|
||||
default => 'xl:w-6/12 lg:w-6/12 md:w-6/12 w-full !self-start',
|
||||
};
|
||||
$featuredName = $featured['name'] ?? null;
|
||||
if (!$featuredName && !empty($featured['url'])) {
|
||||
$host = parse_url($featured['url'], PHP_URL_HOST);
|
||||
$featuredName = $host ? preg_replace('/^www\./', '', $host) : t('Çözüm Ortağı');
|
||||
}
|
||||
@endphp
|
||||
<div class="flex-[0_0_auto] !px-[15px] max-w-full xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !mt-[25px] {{ $colClasses }}">
|
||||
<div class="partners-cta-card">
|
||||
<div class="card-body flex-[1_1_auto] p-[32px] xl:p-[40px] text-center">
|
||||
<div class="partner-card__logo-wrap !mb-3 !max-w-[120px]">
|
||||
@if(!empty($featured['url']))
|
||||
<a href="{{ $featured['url'] }}" target="_blank" rel="nofollow noopener" class="partners-showcase__item" title="{{ $featuredName }}">
|
||||
@else
|
||||
<div class="partners-showcase__item">
|
||||
@endif
|
||||
<img
|
||||
src="{{ asset('storage/' . $featured['logo']) }}"
|
||||
alt="{{ $featuredName }}"
|
||||
loading="lazy"
|
||||
>
|
||||
@if(!empty($featured['url']))
|
||||
</a>
|
||||
@else
|
||||
</div>
|
||||
<h5 class="!mb-0 !text-[0.95rem] !font-bold text-[#1e293b]">{{ $featuredName }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] !mt-[30px] max-w-full {{ !empty($partners) ? 'xl:w-5/12 lg:w-5/12 w-full' : 'w-full col-lg-8 mx-auto text-center' }}">
|
||||
|
||||
Reference in New Issue
Block a user