106 Commits

Author SHA1 Message Date
Ümit Tunç 42fcc5759f fix: set font-display to swap for all custom fonts and remove obsolete Tailwind CSS base styles 2026-07-04 01:12:02 +03:00
Ümit Tunç 161d1c86bc feat: implement multilingual support for sitemap and SEO, update structured data, and improve locale middleware configuration 2026-07-04 00:59:55 +03:00
Ümit Tunç 4af8900a78 feat: add external admin dashboard link to navigation and remove Gantt chart widget from intern applications page 2026-07-02 10:40:10 +03:00
Ümit Tunç abcf105d07 feat: add intern panel navigation link and user password reset CLI command 2026-07-02 10:32:02 +03:00
Ümit Tunç d0292af20c feat: add GitHub username field and implement commit filtering by author in intern dashboard 2026-07-01 23:46:12 +03:00
Ümit Tunç 26670fcca5 feat: implement internship journal approval status tracking and add single-day printing functionality 2026-07-01 23:36:26 +03:00
Ümit Tunç f557d4b91c feat: replace eager in-memory GitHub commit fetching with on-demand retrieval and add a fallback UI for missing commits. 2026-07-01 23:34:42 +03:00
Ümit Tunç 09e92506fd feat: implement tabbed interface to toggle between Gantt chart and intern list in admin dashboard 2026-07-01 23:15:36 +03:00
Ümit Tunç 129dae4570 feat: add journal progress tracking UI for internship applications in the career dashboard 2026-07-01 23:06:13 +03:00
Ümit Tunç f0a9180a54 feat: add journal detail tracking, progress reporting, and notebook signing functionality for intern management 2026-07-01 23:04:23 +03:00
Ümit Tunç a6c250c13e refactor: simplify internship approval process by removing unit approval and adding supervisor name tracking 2026-07-01 22:37:08 +03:00
Ümit Tunç f84f78a138 feat: add approval system for internship journal entries including database fields, admin toggle functionality, and rich-text rendering 2026-07-01 21:58:56 +03:00
Ümit Tunç d9c1c825d9 style: increase container maximum width to 1440px in intern dashboard view 2026-07-01 17:49:06 +03:00
Ümit Tunç 13f8f09524 refactor: move tab logic and implement Quill editor and GitHub commit integration in scratch_test.js 2026-07-01 17:46:57 +03:00
Ümit Tunç 351b9064af feat: integrate Quill editor for internship journal entries, add retroactive field support, and enforce future date restrictions 2026-07-01 17:43:22 +03:00
Ümit Tunç edc843fa1c feat: restrict verification page view to accepted applications only 2026-07-01 17:40:06 +03:00
Ümit Tunç 8cf9e0b191 style: update career portal branding, logo assets, and dashboard text visibility 2026-07-01 17:25:52 +03:00
Ümit Tunç 44852bdc95 feat: add download icon and text to signed internship form button in intern dashboard 2026-07-01 17:21:39 +03:00
Ümit Tunç 7b70621c5a feat: implement interactive intern dashboard journal features and correct resource component namespace 2026-07-01 17:20:19 +03:00
Ümit Tunç 4c5cf1d355 feat: implement internship journal entry system with printable reports and approval workflow 2026-07-01 17:16:41 +03:00
Ümit Tunç 5b46b4ac9e refactor: restrict intern dashboard updates to pending status and automate username assignment from email 2026-07-01 17:06:11 +03:00
Ümit Tunç 32c556a87b feat: add internship workflow guide modal to dashboards and visualize current date in Gantt chart 2026-06-26 16:42:24 +03:00
Ümit Tunç aa6858bc0f chore: reorder DevExtreme script dependencies in gantt partial 2026-06-26 16:01:17 +03:00
Ümit Tunç 04e62ea9b8 refactor: modularize admin dashboard by extracting components into separate partial views 2026-06-26 16:00:10 +03:00
Ümit Tunç 66366aa8b8 refactor: modularize admin dashboard view with partials and update intern parent ID mapping for gantt chart rendering 2026-06-26 15:59:59 +03:00
Ümit Tunç 0e558fd1dc feat: implement intern admin portal with login and dashboard management functionality 2026-06-26 15:54:21 +03:00
Ümit Tunç 01be250e6c feat: add summer background images to assets directory 2026-06-25 16:19:07 +03:00
Ümit Tunç 6614d13be2 refactor: update service cards to full-width mobile layout and apply light theme glass styling 2026-06-23 11:24:06 +03:00
Ümit Tunç 61480399b3 refactor: wrap service cards in anchor tags and update styling for a glassmorphism effect 2026-06-23 11:23:15 +03:00
Ümit Tunç e3e7ac7f18 style: add mobile-specific background image styling to hero section 2026-06-23 11:10:28 +03:00
Ümit Tunç f1c7e57e43 style: refine services card responsive typography and layout styling 2026-06-23 11:06:06 +03:00
Ümit Tunç ad50102c42 feat: add localized routes for privacy policy pages 2026-06-23 10:57:52 +03:00
Ümit Tunç 007da1227d fix: update intern gantt chart scale type from weeks to days 2026-06-16 17:26:32 +03:00
Ümit Tunç 58ae406b36 feat: replace custom gantt chart implementation with DevExtreme widget 2026-06-16 17:14:54 +03:00
Ümit Tunç 794f9556de feat: implement custom responsive gantt chart UI for intern applications widget 2026-06-16 15:28:20 +03:00
Ümit Tunç 6be7f4442a feat: add Gantt chart widget to visualize accepted intern schedules in the admin panel 2026-06-16 15:27:28 +03:00
Ümit Tunç 9361a01c80 feat: update intern dashboard to display signed status and prevent modifications for approved internship forms 2026-06-10 21:02:03 +03:00
Ümit Tunç f9abe3c02a feat: add visual progress timeline to the intern application dashboard 2026-06-10 20:47:57 +03:00
Ümit Tunç efc6b911e7 fix: clear password field state in intern application resource form 2026-06-10 20:44:09 +03:00
Ümit Tunç 793a843be7 fix: remove authorized signature section from application verification view 2026-06-08 19:39:03 +03:00
Ümit Tunç 742303eb73 refactor: remove decorative borders and normalize print layout styling for PDF generation 2026-06-08 19:35:08 +03:00
Ümit Tunç cfce7b8b3f style: convert certificate text elements to uppercase and remove redundant classes 2026-06-08 19:25:34 +03:00
Ümit Tunç 13d12e4f8f style: update career certificate typography with new Cinzel font class and layout adjustments 2026-06-08 19:19:16 +03:00
Ümit Tunç ba6431e500 refactor: remove print button and update decorative underline styling in career verification page 2026-06-08 19:19:11 +03:00
Ümit Tunç eca57bf9cc style: add text-glow-white utility class and update career verification UI typography colors 2026-06-08 19:15:46 +03:00
Ümit Tunç 89b81e6ee8 style: refine verification banner and button UI for improved layout and spacing 2026-06-08 18:53:25 +03:00
Ümit Tunç fa06ba7545 feat: update certificate design with watermark logo, adjusted typography, and improved header layout 2026-06-08 18:51:15 +03:00
Ümit Tunç 4eb0a96832 feat: redesign certificate verification page with new futuristic background asset 2026-06-08 18:46:15 +03:00
Ümit Tunç 3600cc2334 refactor: implement global CSS variable for transcript typography scaling and apply semantic class-based sizing 2026-06-08 18:33:25 +03:00
Ümit Tunç 554f22695d refactor: optimize internship transcript layout and typography for A4 print consistency 2026-06-08 18:32:07 +03:00
Ümit Tunç af82f47ea0 feat: increase verification QR code sizes and adjust PDF image scaling logic 2026-06-08 18:27:34 +03:00
Ümit Tunç 612a24042b refactor: redesign internship verification page with updated layout, enhanced PDF download capabilities, and refined certificate styling 2026-06-08 18:22:39 +03:00
Ümit Tunç 082fb33af3 refactor: migrate career applications to separate job and intern Filament resources 2026-06-08 18:17:56 +03:00
Ümit Tunç 5b07f385b1 refactor: split CareerApplication into separate Job and Intern application resources in Filament and update hero social links 2026-06-08 18:17:51 +03:00
Ümit Tunç aa4a4b3e27 refactor: redesign intern journal timeline component with modernized animations and styles 2026-06-06 01:36:11 +03:00
Ümit Tunç b776ebbd26 refactor: replace inline journal timeline view with a dedicated Livewire component for improved performance and structure 2026-06-06 01:30:15 +03:00
Ümit Tunç 0d3a514456 refactor: organize career application form into tabs and add intern journal timeline component 2026-06-06 01:19:26 +03:00
Ümit Tunç 99e97edfd1 feat: implement carousel-based timeline view for internship dashboard commit history 2026-06-06 01:10:17 +03:00
Ümit Tunç db60909348 refactor: group internship commits by date and update UI to a grid layout 2026-06-06 01:10:13 +03:00
Ümit Tunç 080c39ac88 feat: add github_repo field to career applications and update intern dashboard UI 2026-06-06 01:05:29 +03:00
Ümit Tunç c3f343ad70 feat: add multilingual support to bank accounts and legal information sections 2026-06-02 22:11:42 +03:00
Ümit Tunç fb1224f7c9 feat: add PDF download functionality with loading state and improve print layout styling 2026-06-02 21:26:50 +03:00
Ümit Tunç f68bc87031 style: adjust company info margin to fix letterhead layout positioning 2026-06-02 20:19:07 +03:00
Ümit Tunç fee4c8fa78 style: remove background color from bank account statement description box 2026-06-02 20:17:38 +03:00
Ümit Tunç e8352f599b refactor: modernize bank account page layout with premium tab styling and optimized letterhead print view 2026-06-02 20:16:35 +03:00
Ümit Tunç 80de8dc6f9 feat: implement imprint page with copy-to-clipboard functionality and update corporate templates 2026-06-02 20:06:24 +03:00
Ümit Tunç e4e7599cb2 refactor: extract legal info and letterhead sections into separate partial blade templates 2026-06-02 19:58:15 +03:00
Ümit Tunç 88c1657b6a style: adjust top margin of company info block in bank accounts template 2026-06-02 19:55:26 +03:00
Ümit Tunç f195d22342 feat: implement A4 letterhead preview and print functionality for bank account details 2026-06-02 19:54:39 +03:00
Ümit Tunç 864f80c7b8 style: remove fixed background attachment from hero section 2026-06-02 19:52:38 +03:00
Ümit Tunç d245055bf4 feat: add automated WebP conversion and resizing for hero background images 2026-06-02 06:44:57 +03:00
Ümit Tunç 9c4dc0ff11 style: add hover transition effects to glass card headings 2026-06-02 06:29:17 +03:00
Ümit Tunç 092eb09316 fix: correct typo in CSS transition property and adjust hover brightness filter for service cards 2026-06-02 06:07:34 +03:00
Ümit Tunç 82ea32cd33 feat: add hover brightness transition effect to glass card icons 2026-06-02 06:07:28 +03:00
Ümit Tunç 2ee018c37c feat: apply glassmorphism effect to service cards and align call-to-action buttons 2026-06-02 06:01:59 +03:00
Ümit Tunç 81690db06d feat: make hero background image configurable via settings service 2026-06-02 05:59:43 +03:00
Ümit Tunç a9c872e5b4 refactor: remove redundant call-to-action section from web development template 2026-06-02 05:47:30 +03:00
Ümit Tunç 97e95efb17 refactor: remove all social media link components from header, navbar, and partials view files 2026-06-02 05:47:26 +03:00
Ümit Tunç 181ec53ef0 feat: update internship process workflow and localization labels in mermaid diagrams 2026-06-02 00:00:24 +03:00
Ümit Tunç 0a3feba23a feat: update internship process workflow and localization keys in career module 2026-06-02 00:00:20 +03:00
Ümit Tunç f099d8f46a refactor: update internship application requirements and refine the internal admission flow diagram 2026-06-01 23:46:47 +03:00
Ümit Tunç 36ec1823c8 feat: implement internship application wizard with terms acceptance and multi-step form view 2026-06-01 23:43:59 +03:00
Ümit Tunç 23f24e426b fix: adjust mermaid text styling and defer initialization until fonts are fully loaded to ensure correct layout rendering 2026-06-01 23:39:39 +03:00
Ümit Tunç cf4572cb03 feat: add fullscreen toggle functionality to the Mermaid flowchart card 2026-06-01 23:38:14 +03:00
Ümit Tunç efa575a920 style: update text color of decisionNode in career page mermaid diagram 2026-06-01 23:36:47 +03:00
Ümit Tunç fc2c99f0c2 style: improve readability and visual contrast for career page Mermaid diagrams 2026-06-01 23:36:43 +03:00
Ümit Tunç 3235a700c9 feat: add career application wizard and HR process documentation strings and dynamic views 2026-06-01 23:36:16 +03:00
Ümit Tunç e23ac8c346 feat: add ID photocopy upload field to career application form with validation and database storage 2026-06-01 23:32:52 +03:00
Ümit Tunç 93e45655eb feat: implement multi-step wizard interface for career application form 2026-06-01 23:30:39 +03:00
Ümit Tunç 1b0800beb6 chore: update default testimonial section image path 2026-06-01 23:29:03 +03:00
Ümit Tunç d828df82bb refactor: localize all UI strings in the dijital-donusum product page using the t() helper 2026-06-01 23:20:37 +03:00
Ümit Tunç 690ee11336 docs: update customer testimonial names and company affiliations in digital transformation product page 2026-06-01 23:18:45 +03:00
Ümit Tunç c719e58d4e chore: update partner section header text to localized string 2026-06-01 23:15:59 +03:00
Ümit Tunç 7e2cd6eff4 feat: dynamically render partner logos from CMS in digital transformation product page 2026-06-01 23:15:54 +03:00
Ümit Tunç 0bdf0c3394 feat: add digital transformation product page layout and styles 2026-06-01 23:09:46 +03:00
Ümit Tunç d7200111e2 feat: add digital transformation landing page and link in music production view 2026-06-01 23:09:42 +03:00
Ümit Tunç faf752dd3c feat: redesign music production metadata table with localized labels and styled UI components 2026-06-01 09:16:30 +03:00
Ümit Tunç 31e695cd3a feat: add metadata table component for music production details and update localization labels 2026-06-01 09:02:02 +03:00
Ümit Tunç f77866faa5 refactor: parse track titles for styles, update date formatting, and adjust UI transition animations in music production views 2026-06-01 08:53:46 +03:00
Ümit Tunç fbbed52a3d style: adjust music player hover transformation offsets and scale factor 2026-06-01 08:46:50 +03:00
Ümit Tunç e7ad0123ac refactor: update music production card layout and hover interaction styles 2026-06-01 08:46:21 +03:00
Ümit Tunç f34fc0f2a0 refactor: replace YouTube lightbox with persistent background playback integration and vinyl animation state management 2026-06-01 08:43:16 +03:00
Ümit Tunç 9e6bcc22b6 feat: replace CD mockup with animated vinyl player component in music productions list and detail views 2026-06-01 08:38:21 +03:00
Ümit Tunç 3c023ba588 style: remove backdrop-filter blur from music production overlay 2026-06-01 08:31:44 +03:00
Ümit Tunç ddbfc7e3a7 feat: replace static cover image with interactive 3D CD jewel case mockup and YouTube video lightbox 2026-06-01 08:31:21 +03:00
Ümit Tunç 59d9dfe40d feat: add detailed logging and directory verification for YouTube cover image storage failures 2026-06-01 08:15:49 +03:00
90 changed files with 11543 additions and 33234 deletions
@@ -0,0 +1,77 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class ResetUserPassword extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:reset-password {email? : Kullanıcının e-posta adresi} {password? : Yeni şifre (Belirtilmezse etkileşimli olarak istenir veya otomatik oluşturulur)}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Belirtilen kullanıcının şifresini günceller';
/**
* Execute the console command.
*/
public function handle(): int
{
// E-posta adresini al veya sor
$email = $this->argument('email') ?: $this->ask('Kullanıcının e-posta adresini giriniz:');
if (empty($email)) {
$this->error('E-posta adresi boş olamaz!');
return self::FAILURE;
}
// Email formatını kontrol et
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error('Geçersiz e-posta formatı!');
return self::FAILURE;
}
// Kullanıcıyı bul
$user = User::where('email', $email)->first();
if (!$user) {
$this->error("E-posta adresi '{$email}' ile kayıtlı kullanıcı bulunamadı!");
return self::FAILURE;
}
// Şifreyi al veya sor ya da rastgele oluştur
$password = $this->argument('password');
if ($password === null) {
if ($this->confirm('Yeni şifreyi otomatik rastgele mi üretelim (6 haneli rakamsal)?', true)) {
$password = (string) random_int(100000, 999999);
} else {
$password = $this->secret('Yeni şifreyi giriniz:');
if (empty($password)) {
$this->error('Şifre boş olamaz!');
return self::FAILURE;
}
}
}
// Şifreyi güncelle ve kaydet
$user->password = Hash::make($password);
$user->save();
$this->info("✓ Kullanıcı '{$user->name}' ({$user->email}) şifresi başarıyla güncellendi!");
$this->info("Yeni Şifre: {$password}");
return self::SUCCESS;
}
}
@@ -1,55 +0,0 @@
<?php
namespace App\Filament\Admin\Resources\CareerApplications;
use App\Filament\Admin\Resources\CareerApplications\Pages\CreateCareerApplication;
use App\Filament\Admin\Resources\CareerApplications\Pages\EditCareerApplication;
use App\Filament\Admin\Resources\CareerApplications\Pages\ListCareerApplications;
use App\Filament\Admin\Resources\CareerApplications\Schemas\CareerApplicationForm;
use App\Filament\Admin\Resources\CareerApplications\Tables\CareerApplicationsTable;
use App\Models\CareerApplication;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
class CareerApplicationResource extends Resource
{
protected static ?string $model = CareerApplication::class;
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-user-group';
public static function getNavigationLabel(): string
{
return __('career.navigation_label');
}
public static function getModelLabel(): string
{
return __('career.model_label');
}
public static function getPluralModelLabel(): string
{
return __('career.plural_model_label');
}
public static function form(Schema $schema): Schema
{
return CareerApplicationForm::configure($schema);
}
public static function table(Table $table): Table
{
return CareerApplicationsTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => ListCareerApplications::route('/'),
'create' => CreateCareerApplication::route('/create'),
'edit' => EditCareerApplication::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Admin\Resources\CareerApplications\Pages;
use App\Filament\Admin\Resources\CareerApplications\CareerApplicationResource;
use Filament\Resources\Pages\CreateRecord;
class CreateCareerApplication extends CreateRecord
{
protected static string $resource = CareerApplicationResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Admin\Resources\CareerApplications\Pages;
use App\Filament\Admin\Resources\CareerApplications\CareerApplicationResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListCareerApplications extends ListRecords
{
protected static string $resource = CareerApplicationResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,179 +0,0 @@
<?php
namespace App\Filament\Admin\Resources\CareerApplications\Schemas;
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\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Hash;
use Filament\Actions\Action;
use Filament\Schemas\Components\Utilities\Set;
use Illuminate\Support\Str;
class CareerApplicationForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->label(__('career.name'))
->required()
->disabled(),
TextInput::make('email')
->label(__('career.email'))
->email()
->required()
->disabled(),
TextInput::make('phone')
->label(__('career.phone'))
->disabled(),
FileUpload::make('cv_path')
->label(__('career.cv'))
->disk('public')
->directory('cvs')
->required()
->disabled()
->downloadable(),
Textarea::make('message')
->label(__('career.message'))
->disabled()
->columnSpanFull(),
Select::make('status')
->label(__('career.status'))
->options([
'pending' => __('career.pending'),
'reviewed' => __('career.reviewed'),
'rejected' => __('career.rejected'),
'accepted' => __('career.accepted'),
'waiting_document' => __('career.waiting_document'),
])
->required(),
Section::make('Stajyer Giriş Bilgileri & Belgeleri')
->description('Stajyerin sisteme giriş yapabilmesi için kullanıcı adı, şifre belirleyin ve imzalı staj formunu yükleyin.')
->collapsible()
->schema([
TextInput::make('username')
->label('Kullanıcı Adı')
->unique(ignoreRecord: true)
->nullable(),
TextInput::make('password')
->label('Şifre')
->password()
->revealable()
->dehydrateStateUsing(fn ($state) => filled($state) ? Hash::make($state) : null)
->dehydrated(fn ($state) => filled($state))
->placeholder('Şifreyi değiştirmek istemiyorsanız boş bırakın')
->nullable()
->suffixAction(
Action::make('generatePassword')
->icon('heroicon-m-arrow-path')
->action(fn (Set $set) => $set('password', Str::random(12)))
),
FileUpload::make('to_be_signed_internship_form_path')
->label('İmzalanacak Staj Formu (Stajyerden)')
->disk('public')
->directory('to_be_signed_interns')
->downloadable()
->nullable(),
FileUpload::make('signed_internship_form_path')
->label('İmzalı Staj Formu')
->disk('public')
->directory('signed_interns')
->downloadable()
->nullable(),
DatePicker::make('internship_start_date')
->label('Staj Başlangıç Tarihi')
->live()
->afterStateUpdated(function ($state, $get, Set $set) {
if ($state && $get('internship_total_days')) {
self::calculateEndDate($state, $get('internship_total_days'), $set);
} elseif ($state && $get('internship_end_date')) {
self::calculateTotalDays($state, $get('internship_end_date'), $set);
}
})
->nullable(),
DatePicker::make('internship_end_date')
->label('Staj Bitiş Tarihi')
->live()
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateTotalDays($get('internship_start_date'), $state, $set))
->nullable(),
TextInput::make('internship_total_days')
->label('Toplam Staj Süresi (İş Günü)')
->numeric()
->live()
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateEndDate($get('internship_start_date'), $state, $set))
->nullable(),
])
->columnSpanFull(),
]);
}
public static function calculateTotalDays($start, $end, Set $set): void
{
if (!$start || !$end) {
$set('internship_total_days', null);
return;
}
$startDate = \Carbon\Carbon::parse($start);
$endDate = \Carbon\Carbon::parse($end);
if ($startDate->gt($endDate)) {
$set('internship_total_days', 0);
return;
}
$days = 0;
while ($startDate->lte($endDate)) {
if (!$startDate->isWeekend()) {
$days++;
}
$startDate->addDay();
}
$set('internship_total_days', $days);
}
public static function calculateEndDate($start, $totalDays, Set $set): void
{
if (!$start || !$totalDays || $totalDays <= 0) {
return;
}
$startDate = \Carbon\Carbon::parse($start);
$daysToAdd = intval($totalDays);
$endDate = $startDate->copy();
$count = 0;
$temp = $startDate->copy();
while ($count < $daysToAdd) {
if ($temp->isWeekend()) {
$temp->addDay();
continue;
}
$endDate = $temp->copy();
$temp->addDay();
$count++;
}
$set('internship_end_date', $endDate->format('Y-m-d'));
}
}
@@ -1,124 +0,0 @@
<?php
namespace App\Filament\Admin\Resources\CareerApplications\Tables;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Support\Facades\Storage;
class CareerApplicationsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->label(__('career.name'))
->searchable()
->sortable(),
TextColumn::make('email')
->label(__('career.email'))
->searchable()
->sortable(),
TextColumn::make('phone')
->label(__('career.phone'))
->searchable(),
TextColumn::make('type')
->label(__('career.type'))
->badge()
->color(fn (string $state): string => match ($state) {
'job' => 'success',
'internship' => 'warning',
default => 'gray',
})
->formatStateUsing(fn (string $state): string => __("career.{$state}")),
TextColumn::make('status')
->label(__('career.status'))
->badge()
->color(fn (string $state): string => match ($state) {
'pending' => 'gray',
'reviewed' => 'info',
'rejected' => 'danger',
'accepted' => 'success',
'waiting_document' => 'warning',
default => 'gray',
})
->formatStateUsing(fn (string $state): string => __("career.{$state}")),
TextColumn::make('git_knowledge')
->label(__('career.git_knowledge'))
->badge()
->color(fn ($state) => $state ? 'success' : 'danger')
->formatStateUsing(fn ($state) => $state ? 'Evet' : 'Hayır'),
TextColumn::make('ai_usage')
->label(__('career.ai_usage'))
->badge()
->color(fn ($state) => $state ? 'success' : 'danger')
->formatStateUsing(fn ($state) => $state ? 'Evet' : 'Hayır'),
TextColumn::make('created_at')
->label(__('career.created_at'))
->dateTime('d.m.Y H:i')
->sortable(),
])
->filters([
SelectFilter::make('status')
->label(__('career.status'))
->options([
'pending' => __('career.pending'),
'reviewed' => __('career.reviewed'),
'rejected' => __('career.rejected'),
'accepted' => __('career.accepted'),
'waiting_document' => __('career.waiting_document'),
]),
SelectFilter::make('type')
->label(__('career.type'))
->options([
'job' => __('career.job'),
'internship' => __('career.internship'),
]),
])
->actions([
Action::make('download_cv')
->label(__('career.download_cv'))
->icon('heroicon-o-arrow-down-tray')
->url(fn ($record) => Storage::disk('public')->url($record->cv_path))
->openUrlInNewTab(),
Action::make('download_signed_form')
->label('İmzalı Form İndir')
->icon('heroicon-o-document-check')
->url(fn ($record) => $record->signed_internship_form_path ? Storage::disk('public')->url($record->signed_internship_form_path) : null)
->visible(fn ($record) => !empty($record->signed_internship_form_path))
->openUrlInNewTab(),
Action::make('download_nda')
->label(__('career.nda'))
->icon('heroicon-o-shield-check')
->url(fn ($record) => $record->nda_path ? Storage::disk('public')->url($record->nda_path) : null)
->visible(fn ($record) => $record->nda_path !== null)
->openUrlInNewTab(),
Action::make('download_contract')
->label(__('career.contract'))
->icon('heroicon-o-document-text')
->url(fn ($record) => $record->contract_path ? Storage::disk('public')->url($record->contract_path) : null)
->visible(fn ($record) => $record->contract_path !== null)
->openUrlInNewTab(),
DeleteAction::make(),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
])
->defaultSort('created_at', 'desc');
}
}
@@ -0,0 +1,513 @@
<?php
namespace App\Filament\Admin\Resources\InternApplications;
use App\Models\CareerApplication;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
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\MarkdownEditor;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Components\Livewire;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Filament\Schemas\Components\Utilities\Set;
use Illuminate\Database\Eloquent\Builder;
class InternApplicationResource extends Resource
{
protected static ?string $model = CareerApplication::class;
protected static \BackedEnum|string|null $navigationIcon = 'heroicon-o-academic-cap';
public static function getNavigationLabel(): string
{
return __('career.internship_title', ['default' => 'Staj Başvuruları']);
}
public static function getModelLabel(): string
{
return __('career.internship', ['default' => 'Staj Başvurusu']);
}
public static function getPluralModelLabel(): string
{
return __('career.internship_title', ['default' => 'Staj Başvuruları']);
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->where('type', 'internship');
}
public static function form(Schema $schema): Schema
{
return $schema
->components([
Tabs::make('Tabs')
->tabs([
Tab::make('Kişisel ve Başvuru Bilgileri')
->icon('heroicon-m-user')
->schema([
TextInput::make('name')
->label(__('career.name'))
->required()
->disabled(),
TextInput::make('email')
->label(__('career.email'))
->email()
->required()
->disabled(),
TextInput::make('phone')
->label(__('career.phone'))
->disabled(),
Select::make('status')
->label(__('career.status'))
->options([
'pending' => __('career.pending'),
'reviewed' => __('career.reviewed'),
'rejected' => __('career.rejected'),
'accepted' => __('career.accepted'),
'waiting_document' => __('career.waiting_document'),
])
->required(),
Textarea::make('message')
->label(__('career.message'))
->disabled()
->columnSpanFull(),
])->columns(2),
Tab::make('Staj Belgeleri & Giriş Bilgileri')
->icon('heroicon-m-document-text')
->schema([
FileUpload::make('cv_path')
->label(__('career.cv'))
->disk('public')
->directory('cvs')
->required()
->disabled()
->downloadable(),
TextInput::make('username')
->label('Kullanıcı Adı')
->default(fn ($record) => $record?->email)
->disabled()
->dehydrated()
->autocomplete('new-username'),
TextInput::make('password')
->label('Şifre')
->password()
->revealable()
->autocomplete('new-password')
->formatStateUsing(fn () => null)
->dehydrateStateUsing(fn ($state) => filled($state) ? Hash::make($state) : null)
->dehydrated(fn ($state) => filled($state))
->placeholder('Şifreyi değiştirmek istemiyorsanız boş bırakın')
->nullable()
->suffixAction(
\Filament\Actions\Action::make('generatePassword')
->icon('heroicon-m-arrow-path')
->action(fn (Set $set) => $set('password', Str::random(12)))
),
FileUpload::make('to_be_signed_internship_form_path')
->label('İmzalanacak Staj Formu (Stajyerden)')
->disk('public')
->directory('to_be_signed_interns')
->downloadable()
->nullable(),
FileUpload::make('signed_internship_form_path')
->label('İmzalı Staj Formu')
->disk('public')
->directory('signed_interns')
->downloadable()
->live()
->afterStateUpdated(function ($state, Set $set) {
if ($state) {
$set('status', 'accepted');
}
})
->nullable(),
DatePicker::make('internship_start_date')
->label('Staj Başlangıç Tarihi')
->live()
->afterStateUpdated(function ($state, $get, Set $set) {
if ($state && $get('internship_total_days')) {
self::calculateEndDate($state, $get('internship_total_days'), $set);
} elseif ($state && $get('internship_end_date')) {
self::calculateTotalDays($state, $get('internship_end_date'), $set);
}
})
->nullable(),
DatePicker::make('internship_end_date')
->label('Staj Bitiş Tarihi')
->live()
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateTotalDays($get('internship_start_date'), $state, $set))
->nullable(),
TextInput::make('internship_total_days')
->label('Toplam Staj Süresi (İş Günü)')
->numeric()
->live()
->afterStateUpdated(fn ($state, $get, Set $set) => self::calculateEndDate($get('internship_start_date'), $state, $set))
->nullable(),
])->columns(2),
Tab::make('Staj Günlüğü')
->icon('heroicon-m-squares-plus')
->schema([
TextInput::make('github_repo')
->label('GitHub Depo URL\'si')
->url()
->nullable()
->live(),
Livewire::make(\App\Livewire\InternJournalTimeline::class)
->columnSpanFull()
]),
Tab::make('Staj Defteri & Onay')
->icon('heroicon-o-book-open')
->schema([
\Filament\Forms\Components\Placeholder::make('notebook_view')
->label('Doldurulan Staj Defteri')
->content(function ($record) {
if (!$record) return 'Henüz başvuru bulunmamaktadır.';
$days = \App\Http\Controllers\CareerController::getInternshipDates($record->internship_start_date, $record->internship_total_days);
if (empty($days)) return 'Staj başlangıç tarihi veya süresi girilmemiş.';
$saved = $record->journalEntries()->get()->keyBy('day_number');
$html = '<style>
.rich-text-content p { margin-bottom: 8px; }
.rich-text-content ul { list-style-type: disc; padding-left: 20px; margin-bottom: 8px; }
.rich-text-content ol { list-style-type: decimal; padding-left: 20px; margin-bottom: 8px; }
.rich-text-content li { margin-bottom: 4px; }
</style>';
$html .= '<div class="space-y-4" style="max-height: 400px; overflow-y: auto; padding-right: 10px; border: 1px solid #cbd5e1; border-radius: 8px; padding: 15px;">';
foreach ($days as $d) {
$dayNum = $d['day_number'];
$dateF = $d['formatted_date'];
$entry = $saved->get($dayNum);
$content = $entry ? $entry->content : '';
$isRetro = $entry ? $entry->is_retroactive : false;
$updatedAt = $entry ? $entry->updated_at->format('d.m.Y H:i') : null;
$html .= '<div style="margin-bottom: 12px; padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc;">';
$html .= ' <div style="display:flex; justify-content:between; font-size:11px; font-weight:700; color:#475569; border-bottom:1px solid #e2e8f0; padding-bottom:6px; margin-bottom:8px;">';
$html .= ' <span style="font-weight: 800; color: #2563eb;">' . $dayNum . '. Gün Raporu</span>';
if ($isRetro) {
$html .= ' <span style="margin-left: 10px; background: #fee2e2; color: #991b1b; padding: 1px 6px; border-radius: 4px; font-size: 9px; font-weight: 800;">GERİYE DÖNÜK KAYIT</span>';
}
if ($updatedAt) {
$html .= ' <span style="margin-left: auto;">Son Güncelleme: ' . $updatedAt . '</span>';
} else {
$html .= ' <span style="margin-left: auto;">' . $dateF . '</span>';
}
$html .= ' </div>';
$cleanContent = $content ? strip_tags($content, ['p', 'strong', 'ul', 'li', 'em', 'br', 'b', 'i', 'ol', 'span']) : '<em style="color:#94a3b8;">Rapor yazılmamış</em>';
$html .= ' <div class="rich-text-content" style="font-size:12px; color:#1e293b; line-height:1.5;">' . $cleanContent . '</div>';
if ($entry && trim($content) !== '') {
$supApproved = $entry->supervisor_approved;
$supName = $entry->supervisor_name;
$html .= ' <div style="display:flex; align-items:center; gap:12px; margin-top:12px; padding-top:10px; border-top:1px dashed #e2e8f0; font-size:11px;">';
// Supervisor approval
$supBg = $supApproved ? '#d1fae5' : '#f1f5f9';
$supColor = $supApproved ? '#065f46' : '#64748b';
$supText = $supApproved ? 'Sorumlu Onayladı' . ($supName ? ' (Onaylayan: ' . e($supName) . ')' : '') : 'Sorumlu Onayı Bekliyor';
$html .= ' <span id="sup-badge-' . $entry->id . '" style="background:' . $supBg . '; color:' . $supColor . '; padding: 2px 8px; border-radius: 4px; font-weight: 700;">' . $supText . '</span>';
// Buttons
$supBtnText = $supApproved ? 'Onayı Kaldır' : 'Onayla';
$supBtnBg = $supApproved ? '#ef4444' : '#2563eb';
$html .= ' <button type="button" onclick="toggleApproval(' . $entry->id . ', this)" style="margin-left:auto; background:' . $supBtnBg . '; color:white; border:none; padding:4px 10px; border-radius:6px; font-weight:bold; cursor:pointer; font-size:10px;">Sorumlu ' . $supBtnText . '</button>';
$html .= ' </div>';
}
$html .= '</div>';
}
$html .= '</div>';
// JS handler
$html .= '
<script>
if (typeof window.toggleApproval !== "function") {
window.toggleApproval = function(entryId, btn) {
btn.disabled = true;
btn.style.opacity = "0.5";
fetch("' . route('intern.admin.toggle-journal-approval') . '", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": "' . csrf_token() . '"
},
body: JSON.stringify({
entry_id: entryId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
const badge = document.getElementById("sup-badge-" + entryId);
if (data.status) {
badge.style.background = "#d1fae5";
badge.style.color = "#065f46";
badge.textContent = "Sorumlu Onayladı (Onaylayan: " + data.supervisor_name + ")";
btn.textContent = "Sorumlu Onayı Kaldır";
btn.style.background = "#ef4444";
} else {
badge.style.background = "#f1f5f9";
badge.style.color = "#64748b";
badge.textContent = "Sorumlu Onayı Bekliyor";
btn.textContent = "Sorumlu Onayla";
btn.style.background = "#2563eb";
}
} else {
alert(data.message || "Bir hata oluştu.");
}
})
.catch(err => {
console.error(err);
alert("Bağlantı hatası oluştu.");
})
.finally(() => {
btn.disabled = false;
btn.style.opacity = "1";
});
};
}
</script>
';
// Add preview buttons
$html .= '<div style="margin-top: 15px; display: flex; gap: 10px;">';
$html .= ' <a href="' . route('intern.print-journal') . '?size=a4&intern_id=' . $record->id . '" target="_blank" style="display:inline-flex; align-items:center; justify-content:center; padding: 8px 16px; background:#2563eb; color:white; border-radius:8px; font-weight:bold; font-size:12px; text-decoration:none; box-shadow: 0 1px 3px rgba(37, 99, 235, 0.2);">A4 Defteri Önizle / Yazdır</a>';
$html .= ' <a href="' . route('intern.print-journal') . '?size=a5&intern_id=' . $record->id . '" target="_blank" style="display:inline-flex; align-items:center; justify-content:center; padding: 8px 16px; background:#4b5563; color:white; border-radius:8px; font-weight:bold; font-size:12px; text-decoration:none; box-shadow: 0 1px 3px rgba(75, 85, 99, 0.2);">A5 Defteri Önizle / Yazdır</a>';
$html .= '</div>';
return new \Illuminate\Support\HtmlString($html);
})
->columnSpanFull(),
\Filament\Schemas\Components\Section::make('Onay ve İmza Bilgileri')
->schema([
\Filament\Forms\Components\Toggle::make('notebook_supervisor_signed')
->label('Staj Sorumlusu İmzala / Onayla')
->live(),
TextInput::make('notebook_supervisor_name')
->label('Staj Sorumlusu Adı / Ünvanı')
->placeholder('Örn: Alperen Trunç')
->default('Alperen Trunç'),
\Filament\Forms\Components\Toggle::make('notebook_approved')
->label('Staj Defterini Genel Olarak Onayla')
->columnSpanFull(),
])->columns(2),
]),
Tab::make('Sertifika & Transkript')
->icon('heroicon-o-academic-cap')
->schema([
TextInput::make('certificate_code')
->label('Doğrulama Kodu')
->helperText('Belge kaydedildiğinde benzersiz doğrulama kodu otomatik olarak üretilir.')
->readonly()
->nullable(),
MarkdownEditor::make('transcript_markdown')
->label('Akademik Transkript (Markdown)')
->columnSpanFull()
->default(function () {
return "### STAJ AKADEMİK TRANSKRİPTİ VE PERFORMANS RAPORU\n\n" .
"#### 🛠️ Deneyimlenen Teknolojiler ve Kazanımlar\n" .
"| Modül / Çalışma Alanı | Kullanılan Teknolojiler / Araçlar | Değerlendirme |\n" .
"| --- | --- | --- |\n" .
"| Backend Mimari & API | Laravel framework, RESTful API, MySQL | Başarılı |\n" .
"| Arayüz & UI/UX Uygulamaları | Flutter, CSS, Glassmorphic Tasarım Prensipleri | Üstün Başarı |\n" .
"| Masaüstü & Sistem Entegrasyonu | Electron.js, Git / GitHub | Başarılı |\n" .
"| Takım Çalışması & Proje Yönetimi | Agile / Scrum, Slack, JIRA | Başarılı |\n\n" .
"#### 📊 Performans Değerlendirme Kriterleri\n" .
"| Değerlendirme Kriteri | Puan (100 Üzerinden) | Harf Notu |\n" .
"| --- | --- | --- |\n" .
"| Teknik Sorumluluk ve Görev Bilinci | 95 | AA |\n" .
"| Problem Çözme ve Analitik Düşünme | 90 | BA |\n" .
"| Ekip Çalışması ve İletişim Uyum | 95 | AA |\n" .
"| Öğrenme Hızı ve Adaptasyon | 100 | AA |\n" .
"| **GENEL BAŞARI ORTALAMASI** | **95.00** | **AA (Mükemmel)** |\n\n" .
"#### 📝 Danışman Görüşü ve Değerlendirme Notu\n" .
"\"Stajyerimiz, staj süresi boyunca kendisine verilen görevleri büyük bir titizlikle yerine getirmiştir. Özellikle karşılaştığı teknik problemlere getirdiği pratik çözümler ve yeni teknolojileri öğrenme isteği takdir edilmeye değerdir. Kurumumuz bünyesinde yürüttüğümüz projelere sağladığı katkılardan ötürü teşekkür eder, profesyonel kariyerinde başarılar dileriz.\"";
}),
])->columns(1)
])->columnSpanFull()
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->label(__('career.name'))
->searchable()
->sortable(),
TextColumn::make('email')
->label(__('career.email'))
->searchable()
->sortable(),
TextColumn::make('phone')
->label(__('career.phone'))
->searchable(),
TextColumn::make('status')
->label(__('career.status'))
->badge()
->color(fn (string $state): string => match ($state) {
'pending' => 'gray',
'reviewed' => 'info',
'rejected' => 'danger',
'accepted' => 'success',
'waiting_document' => 'warning',
default => 'gray',
})
->formatStateUsing(fn (string $state): string => __("career.{$state}")),
TextColumn::make('certificate_code')
->label('Sertifika Kodu')
->searchable()
->placeholder('Yok'),
TextColumn::make('created_at')
->label(__('career.created_at'))
->dateTime('d.m.Y H:i')
->sortable(),
])
->filters([
SelectFilter::make('status')
->label(__('career.status'))
->options([
'pending' => __('career.pending'),
'reviewed' => __('career.reviewed'),
'rejected' => __('career.rejected'),
'accepted' => __('career.accepted'),
'waiting_document' => __('career.waiting_document'),
]),
])
->actions([
Action::make('download_cv')
->label(__('career.download_cv'))
->icon('heroicon-o-arrow-down-tray')
->url(fn ($record) => Storage::disk('public')->url($record->cv_path))
->openUrlInNewTab(),
Action::make('download_signed_form')
->label('İmzalı Form İndir')
->icon('heroicon-o-document-check')
->url(fn ($record) => $record->signed_internship_form_path ? Storage::disk('public')->url($record->signed_internship_form_path) : null)
->visible(fn ($record) => !empty($record->signed_internship_form_path))
->openUrlInNewTab(),
Action::make('view_certificate')
->label('Sertifika Doğrulama')
->icon('heroicon-o-academic-cap')
->color('success')
->url(fn ($record) => $record->certificate_code ? route('internship.verify', $record->certificate_code) : null)
->visible(fn ($record) => !empty($record->certificate_code))
->openUrlInNewTab(),
DeleteAction::make(),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
])
->defaultSort('created_at', 'desc');
}
public static function calculateTotalDays($start, $end, Set $set): void
{
if (!$start || !$end) {
$set('internship_total_days', null);
return;
}
$startDate = \Carbon\Carbon::parse($start);
$endDate = \Carbon\Carbon::parse($end);
if ($startDate->gt($endDate)) {
$set('internship_total_days', 0);
return;
}
$days = 0;
while ($startDate->lte($endDate)) {
if (!$startDate->isWeekend()) {
$days++;
}
$startDate->addDay();
}
$set('internship_total_days', $days);
}
public static function calculateEndDate($start, $totalDays, Set $set): void
{
if (!$start || !$totalDays || $totalDays <= 0) {
return;
}
$startDate = \Carbon\Carbon::parse($start);
$daysToAdd = intval($totalDays);
$endDate = $startDate->copy();
$count = 0;
$temp = $startDate->copy();
while ($count < $daysToAdd) {
if ($temp->isWeekend()) {
$temp->addDay();
continue;
}
$endDate = $temp->copy();
$temp->addDay();
$count++;
}
$set('internship_end_date', $endDate->format('Y-m-d'));
}
public static function getPages(): array
{
return [
'index' => Pages\ListInternApplications::route('/'),
'create' => Pages\CreateInternApplication::route('/create'),
'edit' => Pages\EditInternApplication::route('/{record}/edit'),
];
}
}
@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Admin\Resources\InternApplications\Pages;
use App\Filament\Admin\Resources\InternApplications\InternApplicationResource;
use Filament\Resources\Pages\CreateRecord;
class CreateInternApplication extends CreateRecord
{
protected static string $resource = InternApplicationResource::class;
}
@@ -1,14 +1,14 @@
<?php
namespace App\Filament\Admin\Resources\CareerApplications\Pages;
namespace App\Filament\Admin\Resources\InternApplications\Pages;
use App\Filament\Admin\Resources\CareerApplications\CareerApplicationResource;
use App\Filament\Admin\Resources\InternApplications\InternApplicationResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditCareerApplication extends EditRecord
class EditInternApplication extends EditRecord
{
protected static string $resource = CareerApplicationResource::class;
protected static string $resource = InternApplicationResource::class;
protected function getHeaderActions(): array
{
@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Admin\Resources\InternApplications\Pages;
use App\Filament\Admin\Resources\InternApplications\InternApplicationResource;
use Filament\Resources\Pages\ListRecords;
class ListInternApplications extends ListRecords
{
protected static string $resource = InternApplicationResource::class;
}
@@ -0,0 +1,2 @@
<?php
// Bu bileşen devre dışı bırakılmıştır.
@@ -0,0 +1,218 @@
<?php
namespace App\Filament\Admin\Resources\JobApplications;
use App\Models\CareerApplication;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Illuminate\Support\Facades\Storage;
use Illuminate\Database\Eloquent\Builder;
class JobApplicationResource extends Resource
{
protected static ?string $model = CareerApplication::class;
protected static \BackedEnum|string|null $navigationIcon = 'heroicon-o-briefcase';
public static function getNavigationLabel(): string
{
return __('career.job_application_title', ['default' => 'İş Başvuruları']);
}
public static function getModelLabel(): string
{
return __('career.job', ['default' => 'İş Başvurusu']);
}
public static function getPluralModelLabel(): string
{
return __('career.job_application_title', ['default' => 'İş Başvuruları']);
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->where('type', 'job');
}
public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->label(__('career.name'))
->required()
->disabled(),
TextInput::make('email')
->label(__('career.email'))
->email()
->required()
->disabled(),
TextInput::make('phone')
->label(__('career.phone'))
->disabled(),
Select::make('status')
->label(__('career.status'))
->options([
'pending' => __('career.pending'),
'reviewed' => __('career.reviewed'),
'rejected' => __('career.rejected'),
'accepted' => __('career.accepted'),
'waiting_document' => __('career.waiting_document'),
])
->required(),
FileUpload::make('cv_path')
->label(__('career.cv'))
->disk('public')
->directory('cvs')
->required()
->disabled()
->downloadable(),
FileUpload::make('nda_path')
->label(__('career.nda'))
->disk('public')
->directory('ndas')
->disabled()
->downloadable(),
FileUpload::make('contract_path')
->label(__('career.contract'))
->disk('public')
->directory('contracts')
->disabled()
->downloadable(),
FileUpload::make('id_photocopy_path')
->label(__('career.id_photocopy', ['default' => 'Kimlik Fotokopisi']))
->disk('public')
->directory('id_photocopies')
->disabled()
->downloadable(),
Toggle::make('git_knowledge')
->label(__('career.git_knowledge'))
->disabled(),
Toggle::make('ai_usage')
->label(__('career.ai_usage'))
->disabled(),
Textarea::make('message')
->label(__('career.message'))
->disabled()
->columnSpanFull(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->label(__('career.name'))
->searchable()
->sortable(),
TextColumn::make('email')
->label(__('career.email'))
->searchable()
->sortable(),
TextColumn::make('phone')
->label(__('career.phone'))
->searchable(),
TextColumn::make('status')
->label(__('career.status'))
->badge()
->color(fn (string $state): string => match ($state) {
'pending' => 'gray',
'reviewed' => 'info',
'rejected' => 'danger',
'accepted' => 'success',
'waiting_document' => 'warning',
default => 'gray',
})
->formatStateUsing(fn (string $state): string => __("career.{$state}")),
TextColumn::make('git_knowledge')
->label(__('career.git_knowledge'))
->badge()
->color(fn ($state) => $state ? 'success' : 'danger')
->formatStateUsing(fn ($state) => $state ? 'Evet' : 'Hayır'),
TextColumn::make('ai_usage')
->label(__('career.ai_usage'))
->badge()
->color(fn ($state) => $state ? 'success' : 'danger')
->formatStateUsing(fn ($state) => $state ? 'Evet' : 'Hayır'),
TextColumn::make('created_at')
->label(__('career.created_at'))
->dateTime('d.m.Y H:i')
->sortable(),
])
->filters([
SelectFilter::make('status')
->label(__('career.status'))
->options([
'pending' => __('career.pending'),
'reviewed' => __('career.reviewed'),
'rejected' => __('career.rejected'),
'accepted' => __('career.accepted'),
'waiting_document' => __('career.waiting_document'),
]),
])
->actions([
Action::make('download_cv')
->label(__('career.download_cv'))
->icon('heroicon-o-arrow-down-tray')
->url(fn ($record) => Storage::disk('public')->url($record->cv_path))
->openUrlInNewTab(),
Action::make('download_nda')
->label(__('career.nda'))
->icon('heroicon-o-shield-check')
->url(fn ($record) => $record->nda_path ? Storage::disk('public')->url($record->nda_path) : null)
->visible(fn ($record) => $record->nda_path !== null)
->openUrlInNewTab(),
Action::make('download_contract')
->label(__('career.contract'))
->icon('heroicon-o-document-text')
->url(fn ($record) => $record->contract_path ? Storage::disk('public')->url($record->contract_path) : null)
->visible(fn ($record) => $record->contract_path !== null)
->openUrlInNewTab(),
DeleteAction::make(),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
])
->defaultSort('created_at', 'desc');
}
public static function getPages(): array
{
return [
'index' => Pages\ListJobApplications::route('/'),
'create' => Pages\CreateJobApplication::route('/create'),
'edit' => Pages\EditJobApplication::route('/{record}/edit'),
];
}
}
@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Admin\Resources\JobApplications\Pages;
use App\Filament\Admin\Resources\JobApplications\JobApplicationResource;
use Filament\Resources\Pages\CreateRecord;
class CreateJobApplication extends CreateRecord
{
protected static string $resource = JobApplicationResource::class;
}
@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Admin\Resources\JobApplications\Pages;
use App\Filament\Admin\Resources\JobApplications\JobApplicationResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditJobApplication extends EditRecord
{
protected static string $resource = JobApplicationResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Admin\Resources\JobApplications\Pages;
use App\Filament\Admin\Resources\JobApplications\JobApplicationResource;
use Filament\Resources\Pages\ListRecords;
class ListJobApplications extends ListRecords
{
protected static string $resource = JobApplicationResource::class;
}
+545
View File
@@ -41,6 +41,7 @@ class CareerController extends Controller
if ($request->type === 'job') {
$rules['nda'] = 'required|file|mimes:pdf|max:5120';
$rules['contract'] = 'required|file|mimes:pdf|max:5120';
$rules['id_photocopy'] = 'required|file|mimes:pdf,jpg,jpeg,png|max:5120'; // Max 5MB
$rules['git_knowledge'] = 'accepted';
$rules['ai_usage'] = 'accepted';
}
@@ -50,6 +51,7 @@ class CareerController extends Controller
$cvPath = $request->file('cv') ? $request->file('cv')->store('cvs', 'public') : null;
$ndaPath = $request->file('nda') ? $request->file('nda')->store('ndas', 'public') : null;
$contractPath = $request->file('contract') ? $request->file('contract')->store('contracts', 'public') : null;
$idPhotocopyPath = $request->file('id_photocopy') ? $request->file('id_photocopy')->store('id_photocopies', 'public') : null;
CareerApplication::create([
'name' => $request->name,
@@ -59,6 +61,7 @@ class CareerController extends Controller
'cv_path' => $cvPath,
'nda_path' => $ndaPath,
'contract_path' => $contractPath,
'id_photocopy_path' => $idPhotocopyPath,
'git_knowledge' => $request->has('git_knowledge'),
'ai_usage' => $request->has('ai_usage'),
'message' => $request->message,
@@ -119,8 +122,13 @@ class CareerController extends Controller
}
$intern = CareerApplication::findOrFail(session('intern_id'));
$days = self::getInternshipDates($intern->internship_start_date, $intern->internship_total_days);
$savedEntries = $intern->journalEntries()->get()->keyBy('day_number');
return view('front.career.intern_dashboard', [
'intern' => $intern,
'days' => $days,
'savedEntries' => $savedEntries,
'meta' => [
'title' => 'Stajyer Paneli',
'description' => 'Belgelerinizi buradan indirebilirsiniz.',
@@ -183,6 +191,7 @@ class CareerController extends Controller
'internship_start_date' => $request->internship_start_date,
'internship_end_date' => $endDate->format('Y-m-d'),
'internship_total_days' => $daysToAdd,
'status' => 'waiting_document',
]);
return redirect()->back()->with('success', 'İmzalanacak staj formunuz ve staj tarihleri başarıyla kaydedildi.');
@@ -191,9 +200,545 @@ class CareerController extends Controller
return redirect()->back()->with('error', 'Dosya yüklenirken bir hata oluştu.');
}
public function saveGithubRepo(Request $request)
{
if (!session()->has('intern_id')) {
return redirect()->route('intern.login')->with('error', 'Lütfen önce giriş yapın.');
}
$request->validate([
'github_repo' => 'required|string|url|max:255',
'github_username' => 'nullable|string|max:100',
], [
'github_repo.required' => 'Lütfen Github depo URL\'sini girin.',
'github_repo.url' => 'Geçerli bir URL girilmelidir.',
'github_repo.max' => 'Github depo URL\'si en fazla 255 karakter olabilir.',
'github_username.max' => 'Github kullanıcı adı en fazla 100 karakter olabilir.',
]);
$intern = CareerApplication::findOrFail(session('intern_id'));
// Sanitize & format github URL
$repoUrl = trim($request->github_repo);
$username = trim($request->github_username);
$intern->update([
'github_repo' => $repoUrl,
'github_username' => $username ?: null,
]);
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Github bilgileri başarıyla güncellendi.',
'github_repo' => $repoUrl,
'github_username' => $username ?: null,
]);
}
return redirect()->back()->with('success', 'Github bilgileri başarıyla güncellendi.');
}
public function downloadMarkdown()
{
if (request()->has('intern_id') && auth()->check() && auth()->user()->hasRole('super_admin')) {
$internId = request('intern_id');
} else {
if (!session()->has('intern_id')) {
return redirect()->route('intern.login')->with('error', 'Lütfen önce giriş yapın.');
}
$internId = session('intern_id');
}
$intern = CareerApplication::findOrFail($internId);
$repo = $intern->github_repo;
if (!$repo) {
return redirect()->back()->with('error', 'Lütfen önce Github deposunu tanımlayın.');
}
// Parse owner and repo
// E.g., https://github.com/owner/repo or owner/repo
$repoClean = str_replace(['https://github.com/', 'http://github.com/', 'https://www.github.com/', 'http://www.github.com/'], '', $repo);
$repoClean = trim($repoClean, '/');
$parts = explode('/', $repoClean);
if (count($parts) < 2) {
return redirect()->back()->with('error', 'Geçersiz Github depo formatı.');
}
$owner = $parts[0];
$repoName = explode('.', $parts[1])[0]; // Remove .git if exists
// Fetch commits
$url = "https://api.github.com/repos/{$owner}/{$repoName}/commits?per_page=100";
$opts = [
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: PHP-Github-Client',
]
]
];
$context = stream_context_create($opts);
$response = @file_get_contents($url, false, $context);
if ($response === false) {
return redirect()->back()->with('error', 'Github API\'den commit bilgileri alınamadı. Deponuzun public olduğundan emin olun.');
}
$commits = json_decode($response, true);
if (!is_array($commits)) {
return redirect()->back()->with('error', 'Github API yanıtı çözümlenemedi.');
}
// Sort commits chronological (ascending)
$commits = array_reverse($commits);
// Group commits by date (YYYY-MM-DD)
$grouped = [];
foreach ($commits as $c) {
$dateStr = $c['commit']['author']['date'] ?? '';
if ($dateStr) {
$dateOnly = \Carbon\Carbon::parse($dateStr)->format('Y-m-d');
$grouped[$dateOnly][] = $c;
}
}
// Sort dates chronological (ascending)
ksort($grouped);
// Generate Markdown content
$md = "# Staj Günlüğü - " . $intern->name . "\n";
$md .= "Depo: https://github.com/{$owner}/{$repoName}\n\n";
$day = 1;
foreach ($grouped as $date => $dayCommits) {
$formattedDate = \Carbon\Carbon::parse($date)->format('d.m.Y');
$md .= "## {$day}. Gün ({$formattedDate})\n";
foreach ($dayCommits as $c) {
$message = trim($c['commit']['message'] ?? '');
$time = \Carbon\Carbon::parse($c['commit']['author']['date'] ?? '')->format('H:i');
$hash = substr($c['sha'] ?? '', 0, 7);
$md .= "- [{$time}] `{$hash}`: {$message}\n";
}
$md .= "\n";
$day++;
}
$fileName = str()->slug($intern->name) . "-staj-gunlugu.md";
return response($md, 200, [
'Content-Type' => 'text/markdown; charset=UTF-8',
'Content-Disposition' => 'attachment; filename="' . $fileName . '"',
]);
}
public function internLogout()
{
session()->forget('intern_id');
return redirect()->route('intern.login')->with('success', 'Başarıyla çıkış yapıldı.');
}
public function verifyCertificate($code)
{
$application = CareerApplication::where('certificate_code', $code)
->where('type', 'internship')
->firstOrFail();
$days = self::getInternshipDates($application->internship_start_date, $application->internship_total_days);
$savedEntries = $application->journalEntries()->get()->keyBy('day_number');
return view('front.career.verify', [
'application' => $application,
'days' => $days,
'savedEntries' => $savedEntries,
'meta' => [
'title' => 'Staj Bitirme Sertifikası Doğrulama - ' . $application->name,
'description' => $application->name . ' isimli stajyerimizin staj bitirme sertifikası ve performans raporu doğrulama sayfası.',
]
]);
}
public function internAdminLoginForm()
{
if (auth()->check() && auth()->user()->hasRole('super_admin')) {
return redirect()->route('intern.admin.dashboard');
}
return view('front.career.admin_login', [
'meta' => [
'title' => 'Yönetici Girişi - Staj Takip',
'description' => 'Stajyerleri yönetmek için giriş yapın.',
]
]);
}
public function internAdminLogin(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required|string',
]);
if (auth()->attempt($request->only('email', 'password'))) {
if (auth()->user()->hasRole('super_admin')) {
return redirect()->route('intern.admin.dashboard')->with('success', 'Yönetici girişi başarıyla gerçekleştirildi.');
}
auth()->logout();
return redirect()->back()
->withInput($request->only('email'))
->withErrors([
'email' => 'Bu panele erişmek için super_admin yetkinizin olması gerekir.',
]);
}
return redirect()->back()
->withInput($request->only('email'))
->withErrors([
'email' => 'Girdiğiniz bilgiler eşleşmedi veya hatalı.',
]);
}
public function internAdminDashboard()
{
if (!auth()->check() || !auth()->user()->hasRole('super_admin')) {
return redirect()->route('intern.admin.login')->with('error', 'Lütfen önce yönetici olarak giriş yapın.');
}
$allInterns = CareerApplication::where('type', 'internship')
->with(['journalEntries'])
->orderBy('created_at', 'desc')
->get();
$ganttInterns = CareerApplication::where('type', 'internship')
->where('status', 'accepted')
->whereNotNull('internship_start_date')
->whereNotNull('internship_end_date')
->orderBy('internship_start_date', 'asc')
->get()
->map(function ($intern) {
return [
'id' => $intern->id,
'parentId' => null,
'title' => $intern->name,
'start' => $intern->internship_start_date,
'end' => $intern->internship_end_date,
'progress' => 100
];
});
return view('front.career.admin_dashboard', [
'allInterns' => $allInterns,
'ganttInterns' => $ganttInterns,
'meta' => [
'title' => 'Staj Yönetici Paneli',
'description' => 'Stajyer bilgilerini ve takvimlerini izleyin.',
]
]);
}
public function internAdminLogout()
{
auth()->logout();
return redirect()->route('intern.admin.login')->with('success', 'Yönetici oturumu sonlandırıldı.');
}
public static function getInternshipDates($startDate, $totalDays)
{
if (!$startDate || !$totalDays || $totalDays <= 0) {
return [];
}
$dates = [];
$temp = \Carbon\Carbon::parse($startDate);
$daysToAdd = intval($totalDays);
$count = 0;
while ($count < $daysToAdd) {
if ($temp->isWeekend()) {
$temp->addDay();
continue;
}
$dates[] = [
'day_number' => $count + 1,
'date' => $temp->format('Y-m-d'),
'formatted_date' => $temp->format('d.m.Y'),
];
$temp->addDay();
$count++;
}
return $dates;
}
public function saveJournalEntry(Request $request)
{
if (!session()->has('intern_id')) {
return response()->json(['success' => false, 'message' => 'Lütfen giriş yapın.'], 401);
}
$intern = CareerApplication::findOrFail(session('intern_id'));
$request->validate([
'day_number' => 'required|integer|min:1',
'date' => 'required|date',
'content' => 'nullable|string',
]);
$dateObj = \Carbon\Carbon::parse($request->date);
// 1. Prevent future entries
if ($dateObj->isFuture()) {
return response()->json(['success' => false, 'message' => 'İleriye dönük staj günleri için defter doldurulamaz.'], 422);
}
// 2. Prevent weekend entries
if ($dateObj->isWeekend()) {
return response()->json(['success' => false, 'message' => 'Hafta sonu günlerine staj günlüğü girilemez.'], 422);
}
// 3. Determine if retroactive
$isRetroactive = $dateObj->lt(\Carbon\Carbon::today());
$entry = \App\Models\InternshipJournalEntry::updateOrCreate([
'career_application_id' => $intern->id,
'day_number' => $request->day_number,
'date' => $request->date,
], [
'content' => $request->content,
'is_retroactive' => $isRetroactive,
'supervisor_approved' => false,
'supervisor_name' => null,
]);
return response()->json([
'success' => true,
'message' => $request->day_number . '. Gün kaydı başarıyla kaydedildi.' . ($isRetroactive ? ' (Geriye Dönük Kayıt)' : ''),
'entry' => $entry
]);
}
public function printJournal(Request $request)
{
if (request()->has('intern_id') && auth()->check() && auth()->user()->hasRole('super_admin')) {
$internId = request('intern_id');
} else {
if (!session()->has('intern_id')) {
return redirect()->route('intern.login')->with('error', 'Lütfen önce giriş yapın.');
}
$internId = session('intern_id');
}
$intern = CareerApplication::findOrFail($internId);
$size = $request->query('size', 'a4');
if (!in_array($size, ['a4', 'a5'])) {
$size = 'a4';
}
$days = self::getInternshipDates($intern->internship_start_date, $intern->internship_total_days);
$dayFilter = $request->query('day');
if ($dayFilter) {
$days = array_filter($days, function($d) use ($dayFilter) {
return $d['day_number'] == $dayFilter;
});
$days = array_values($days);
}
$savedEntries = $intern->journalEntries()->get()->keyBy('day_number');
return view('front.career.print', [
'intern' => $intern,
'days' => $days,
'savedEntries' => $savedEntries,
'size' => $size,
]);
}
public function getJournalEntry(Request $request)
{
if (!auth()->check()) {
return response()->json(['success' => false, 'message' => 'Yetkisiz işlem.'], 403);
}
$request->validate([
'intern_id' => 'required|integer|exists:career_applications,id',
'date' => 'required|date',
]);
$intern = \App\Models\CareerApplication::findOrFail($request->intern_id);
$days = self::getInternshipDates($intern->internship_start_date, $intern->internship_total_days);
$dayNum = null;
$dateFormatted = null;
foreach ($days as $d) {
if ($d['date'] === $request->date) {
$dayNum = $d['day_number'];
$dateFormatted = $d['formatted_date'];
break;
}
}
if (!$dayNum) {
return response()->json([
'success' => false,
'message' => 'Seçilen tarih staj dönemi dışındadır veya haftasonudur.'
], 422);
}
$entry = $intern->journalEntries()->where('date', $request->date)->first();
return response()->json([
'success' => true,
'intern_name' => $intern->name,
'day_number' => $dayNum,
'date' => $request->date,
'date_formatted' => $dateFormatted,
'entry' => $entry ? [
'id' => $entry->id,
'content' => $entry->content,
'is_retroactive' => $entry->is_retroactive,
'supervisor_approved' => $entry->supervisor_approved,
'supervisor_name' => $entry->supervisor_name,
] : null
]);
}
public function toggleJournalApproval(Request $request)
{
if (!auth()->check()) {
return response()->json(['success' => false, 'message' => 'Yetkisiz işlem.'], 403);
}
$request->validate([
'entry_id' => 'required|integer|exists:internship_journal_entries,id',
]);
$entry = \App\Models\InternshipJournalEntry::findOrFail($request->entry_id);
$entry->supervisor_approved = !$entry->supervisor_approved;
if ($entry->supervisor_approved) {
$entry->supervisor_name = auth()->user()->name;
} else {
$entry->supervisor_name = null;
}
$entry->save();
return response()->json([
'success' => true,
'status' => $entry->supervisor_approved,
'supervisor_name' => $entry->supervisor_name,
'message' => 'Staj sorumlusu onayı güncellendi.'
]);
}
public function getInternJournalDetails(Request $request)
{
if (!auth()->check()) {
return response()->json(['success' => false, 'message' => 'Yetkisiz işlem.'], 403);
}
$request->validate([
'intern_id' => 'required|integer|exists:career_applications,id',
]);
$intern = CareerApplication::findOrFail($request->intern_id);
// Get all days calculated
$days = self::getInternshipDates($intern->internship_start_date, $intern->internship_total_days);
// Get saved journal entries
$savedEntries = $intern->journalEntries()->get()->keyBy('day_number');
// Prepare entries list matched by day number
$entries = [];
$filledDaysCount = 0;
foreach ($days as $d) {
$dayNum = $d['day_number'];
$entry = $savedEntries->get($dayNum);
$hasSaved = $entry && trim($entry->content) !== '';
if ($hasSaved) {
$filledDaysCount++;
}
$entries[] = [
'day_number' => $dayNum,
'date' => $d['date'],
'formatted_date' => $d['formatted_date'],
'filled' => $hasSaved,
'entry_id' => $entry ? $entry->id : null,
'content' => $entry ? $entry->content : null,
'is_retroactive' => $entry ? $entry->is_retroactive : false,
'supervisor_approved' => $entry ? (bool)$entry->supervisor_approved : false,
'supervisor_name' => $entry ? $entry->supervisor_name : null,
];
}
return response()->json([
'success' => true,
'intern' => [
'id' => $intern->id,
'name' => $intern->name,
'email' => $intern->email,
'phone' => $intern->phone,
'start_date' => $intern->internship_start_date,
'end_date' => $intern->internship_end_date,
'total_days' => $intern->internship_total_days,
'filled_days' => $filledDaysCount,
'notebook_supervisor_signed' => (bool)$intern->notebook_supervisor_signed,
'notebook_supervisor_name' => $intern->notebook_supervisor_name,
'notebook_unit_signed' => (bool)$intern->notebook_unit_signed,
'notebook_unit_name' => $intern->notebook_unit_name,
'notebook_approved' => (bool)$intern->notebook_approved,
],
'entries' => $entries,
]);
}
public function toggleNotebookSignature(Request $request)
{
if (!auth()->check()) {
return response()->json(['success' => false, 'message' => 'Yetkisiz işlem.'], 403);
}
$request->validate([
'intern_id' => 'required|integer|exists:career_applications,id',
'type' => 'required|string|in:supervisor,unit,approved',
'signed' => 'required|boolean',
'name' => 'nullable|string|max:255',
]);
$intern = CareerApplication::findOrFail($request->intern_id);
$type = $request->type;
$signed = $request->signed;
$name = $request->name ?: auth()->user()->name;
if ($type === 'supervisor') {
$intern->notebook_supervisor_signed = $signed;
$intern->notebook_supervisor_name = $signed ? $name : null;
} elseif ($type === 'unit') {
$intern->notebook_unit_signed = $signed;
$intern->notebook_unit_name = $signed ? $name : null;
} elseif ($type === 'approved') {
$intern->notebook_approved = $signed;
}
$intern->save();
return response()->json([
'success' => true,
'message' => 'Staj defteri onay durumu güncellendi.',
'intern' => [
'notebook_supervisor_signed' => (bool)$intern->notebook_supervisor_signed,
'notebook_supervisor_name' => $intern->notebook_supervisor_name,
'notebook_unit_signed' => (bool)$intern->notebook_unit_signed,
'notebook_unit_name' => $intern->notebook_unit_name,
'notebook_approved' => (bool)$intern->notebook_approved,
]
]);
}
}
+38 -6
View File
@@ -12,24 +12,50 @@ class SitemapController extends Controller
public function index(): Response
{
$urls = [];
$activeLanguages = [];
if (class_exists(\App\Models\Language::class)) {
$activeLanguages = \App\Models\Language::where('is_active', true)->get();
}
$getAlternates = function (string $baseUrl) use ($activeLanguages) {
$alternates = [];
foreach ($activeLanguages as $lang) {
$connector = str_contains($baseUrl, '?') ? '&' : '?';
$alternates[] = [
'hreflang' => $lang->code,
'href' => $baseUrl . $connector . 'locale=' . $lang->code,
];
}
$alternates[] = [
'hreflang' => 'x-default',
'href' => $baseUrl,
];
return $alternates;
};
// 1. Static & Main Pages
$homeUrl = url('/');
$urls[] = [
'loc' => url('/'),
'loc' => $homeUrl,
'alternates' => $getAlternates($homeUrl),
'lastmod' => now()->startOfDay()->toAtomString(),
'changefreq' => 'daily',
'priority' => '1.0',
];
$blogIndexUrl = route('blog.index');
$urls[] = [
'loc' => route('blog.index'),
'loc' => $blogIndexUrl,
'alternates' => $getAlternates($blogIndexUrl),
'lastmod' => now()->startOfDay()->toAtomString(),
'changefreq' => 'weekly',
'priority' => '0.8',
];
$careerIndexUrl = route('career.index');
$urls[] = [
'loc' => route('career.index'),
'loc' => $careerIndexUrl,
'alternates' => $getAlternates($careerIndexUrl),
'lastmod' => now()->startOfMonth()->toAtomString(),
'changefreq' => 'monthly',
'priority' => '0.5',
@@ -40,8 +66,10 @@ class SitemapController extends Controller
->where('is_homepage', false)
->get();
foreach ($pages as $page) {
$pageUrl = url($page->slug);
$urls[] = [
'loc' => url($page->slug),
'loc' => $pageUrl,
'alternates' => $getAlternates($pageUrl),
'lastmod' => $page->updated_at->toAtomString(),
'changefreq' => 'weekly',
'priority' => '0.7',
@@ -52,8 +80,10 @@ class SitemapController extends Controller
if (class_exists(Blog::class)) {
$posts = Blog::published()->get();
foreach ($posts as $post) {
$postUrl = route('blog.show', $post->slug);
$urls[] = [
'loc' => route('blog.show', $post->slug),
'loc' => $postUrl,
'alternates' => $getAlternates($postUrl),
'lastmod' => $post->updated_at->toAtomString(),
'changefreq' => 'weekly',
'priority' => '0.6',
@@ -65,8 +95,10 @@ class SitemapController extends Controller
if (class_exists(Product::class)) {
$products = Product::where('is_active', true)->get();
foreach ($products as $product) {
$productUrl = route('products.show', $product->slug);
$urls[] = [
'loc' => route('products.show', $product->slug),
'loc' => $productUrl,
'alternates' => $getAlternates($productUrl),
'lastmod' => $product->updated_at->toAtomString(),
'changefreq' => 'weekly',
'priority' => '0.7',
+19 -11
View File
@@ -16,10 +16,25 @@ class SetLocale
*/
public function handle(Request $request, Closure $next): Response
{
// Session'dan locale'i al, yoksa varsayılan dil kodunu kullan
$locale = session('locale');
// Aktif dilleri veritabanından kontrol et
if (function_exists('available_language_codes')) {
$availableLocales = available_language_codes();
} else {
$availableLocales = ['tr', 'en', 'de', 'ar', 'se', 'ru'];
}
// Query parametresinden al, yoksa session'dan al
$queryLocale = $request->query('locale') ?? $request->query('lang');
$locale = null;
if ($queryLocale && in_array($queryLocale, $availableLocales)) {
$locale = $queryLocale;
session(['locale' => $locale]);
} else {
$locale = session('locale');
}
// Eğer session'da locale yoksa, varsayılan dil kodunu kullan
// Eğer locale hala atanmadıysa varsayılan dil kodunu kullan
if (!$locale) {
if (function_exists('default_language_code')) {
$locale = default_language_code();
@@ -28,14 +43,7 @@ class SetLocale
}
}
// Aktif dilleri veritabanından kontrol et
if (function_exists('available_language_codes')) {
$availableLocales = available_language_codes();
} else {
$availableLocales = ['tr', 'en'];
}
// Locale'i aktif diller arasında kontrol et
// Locale'i aktif diller arasında kontrol et ve ata
if (in_array($locale, $availableLocales)) {
App::setLocale($locale);
} else {
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Livewire;
use Livewire\Component;
use Illuminate\Database\Eloquent\Model;
class InternJournalTimeline extends Component
{
public ?Model $record = null;
public function getGithubRepoProperty(): ?string
{
return $this->record?->github_repo;
}
public function render()
{
return view('livewire.intern-journal-timeline');
}
}
+36
View File
@@ -17,6 +17,7 @@ class CareerApplication extends Model
'cv_path',
'nda_path',
'contract_path',
'id_photocopy_path',
'git_knowledge',
'ai_usage',
'message',
@@ -28,8 +29,40 @@ class CareerApplication extends Model
'internship_start_date',
'internship_end_date',
'internship_total_days',
'github_repo',
'github_username',
'certificate_code',
'transcript_markdown',
'notebook_supervisor_signed',
'notebook_supervisor_name',
'notebook_unit_signed',
'notebook_unit_name',
'notebook_approved',
];
/**
* Get the journal entries for the internship application.
*/
public function journalEntries()
{
return $this->hasMany(InternshipJournalEntry::class);
}
protected static function booted()
{
static::saving(function ($model) {
if ($model->type === 'internship') {
$model->username = $model->email;
if (!$model->certificate_code) {
do {
$code = 'TRN-' . date('Y') . '-' . strtoupper(\Illuminate\Support\Str::random(4)) . '-' . strtoupper(\Illuminate\Support\Str::random(4));
} while (static::where('certificate_code', $code)->exists());
$model->certificate_code = $code;
}
}
});
}
/**
* Get the attributes that should be cast.
*
@@ -39,6 +72,9 @@ class CareerApplication extends Model
{
return [
'password' => 'hashed',
'notebook_supervisor_signed' => 'boolean',
'notebook_unit_signed' => 'boolean',
'notebook_approved' => 'boolean',
];
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class InternshipJournalEntry extends Model
{
use HasFactory;
protected $fillable = [
'career_application_id',
'day_number',
'date',
'content',
'is_retroactive',
'supervisor_approved',
'unit_approved',
'supervisor_name',
];
protected $casts = [
'is_retroactive' => 'boolean',
'supervisor_approved' => 'boolean',
'unit_approved' => 'boolean',
];
/**
* Get the career application that owns this journal entry.
*/
public function careerApplication()
{
return $this->belongsTo(CareerApplication::class);
}
}
+103 -5
View File
@@ -75,17 +75,115 @@ class Setting extends Model
$this->attributes['value'] = $value ? '1' : '0';
}
/**
* Set value from different field names
*/
public function setValueFileAttribute($value)
{
// Filament bazen dosya yolunu array olarak döndürebilir, bu durumda ilk elemanı alıyoruz
if (is_array($value)) {
$this->attributes['value'] = array_values($value)[0] ?? null;
$filePath = array_values($value)[0] ?? null;
} else {
$this->attributes['value'] = $value;
$filePath = $value;
}
if ($filePath && $this->key === 'hero_bg') {
$filePath = $this->processHeroBackground($filePath);
}
$this->attributes['value'] = $filePath;
}
/**
* Process the hero background image: convert to webp, resize if necessary, and compress.
*/
protected function processHeroBackground($filePath)
{
try {
$disk = \Illuminate\Support\Facades\Storage::disk('public');
if (!$disk->exists($filePath)) {
return $filePath;
}
$absolutePath = $disk->path($filePath);
// Get image info
$imageInfo = @getimagesize($absolutePath);
if (!$imageInfo) {
return $filePath;
}
$mimeType = $imageInfo['mime'];
// Create GD image resource based on mime type
switch ($mimeType) {
case 'image/jpeg':
case 'image/jpg':
$image = @imagecreatefromjpeg($absolutePath);
break;
case 'image/png':
$image = @imagecreatefrompng($absolutePath);
break;
case 'image/webp':
$image = @imagecreatefromwebp($absolutePath);
break;
case 'image/gif':
$image = @imagecreatefromgif($absolutePath);
break;
default:
return $filePath;
}
if (!$image) {
return $filePath;
}
// Preserve transparency or handle it for webp if needed
imagealphablending($image, true);
imagesavealpha($image, true);
$originalWidth = imagesx($image);
$originalHeight = imagesy($image);
// Target dimensions: Hero bg size, max width 1920px
$maxWidth = 1920;
if ($originalWidth > $maxWidth) {
$newWidth = $maxWidth;
$newHeight = (int) (($originalHeight / $originalWidth) * $maxWidth);
$resizedImage = imagecreatetruecolor($newWidth, $newHeight);
// Preserve transparency
imagealphablending($resizedImage, false);
imagesavealpha($resizedImage, true);
// Resize
imagecopyresampled($resizedImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $originalWidth, $originalHeight);
imagedestroy($image);
$image = $resizedImage;
}
// Generate new webp path
$pathInfo = pathinfo($filePath);
$newWebpPath = $pathInfo['dirname'] . '/' . $pathInfo['filename'] . '.webp';
$absoluteWebpPath = $disk->path($newWebpPath);
// Save as webp with 80% quality (excellent quality-to-size ratio)
if (imagewebp($image, $absoluteWebpPath, 80)) {
imagedestroy($image);
// If the file extension changed, delete the original file
if ($newWebpPath !== $filePath) {
$disk->delete($filePath);
}
return $newWebpPath;
}
imagedestroy($image);
} catch (\Exception $e) {
// Log error or fallback to original
\Illuminate\Support\Facades\Log::error('Hero background image processing failed: ' . $e->getMessage());
}
return $filePath;
}
/**
@@ -85,6 +85,13 @@ class AdminPanelProvider extends PanelProvider
->sort(100); // En sonda görünsün
})->toArray()
])
->navigationItems([
\Filament\Navigation\NavigationItem::make('Admin Stajyer Paneli')
->url('https://truncgil.com/stajyer/admin/panel')
->openUrlInNewTab()
->icon('heroicon-o-academic-cap')
->sort(1000),
])
->assets([
\Filament\Support\Assets\Css::make('citrus', resource_path('css/citrus.css')),
]);
+31 -2
View File
@@ -360,19 +360,48 @@ class YouTubeService
$response = Http::timeout(30)->get($imageUrl);
if ($response->failed()) {
Log::warning('YouTube cover image fetch HTTP request failed', [
'video_id' => $videoId,
'url' => $imageUrl,
'status' => $response->status(),
]);
return null;
}
$extension = $this->guessImageExtension($response->header('Content-Type'));
$path = 'music-productions/covers/youtube-' . $videoId . '.' . $extension;
Storage::disk('public')->put($path, $response->body());
$absolutePath = Storage::disk('public')->path($path);
$directory = dirname($absolutePath);
if (!is_dir($directory)) {
@mkdir($directory, 0775, true);
}
if (is_dir($directory) && !is_writable($directory)) {
Log::error('YouTube cover directory is not writable. Check folder ownership and permissions.', [
'directory' => $directory,
'owner' => function_exists('posix_getpwuid') ? posix_getpwuid(fileowner($directory))['name'] : fileowner($directory),
'perms' => substr(sprintf('%o', fileperms($directory)), -4),
]);
}
$stored = Storage::disk('public')->put($path, $response->body());
if (!$stored) {
Log::error('Failed to store YouTube cover image via storage disk.', [
'path' => $path,
'video_id' => $videoId,
]);
return null;
}
return $path;
} catch (\Throwable $exception) {
Log::warning('YouTube cover download failed', [
Log::error('YouTube cover download failed with exception', [
'video_id' => $videoId,
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString(),
]);
return null;
+1
View File
@@ -110,6 +110,7 @@ class PageTemplateHero
'corporate.partners' => 'Çözüm Ortaklarımız (Kurumsal)',
'corporate.bank-accounts' => 'Banka Bilgilerimiz (Kurumsal)',
'corporate.online-payment' => 'Online Ödeme (Kurumsal)',
'corporate.imprint' => 'Künye (Kurumsal)',
];
$fromConfig = collect(config('page_templates.labels', []))
+37 -1
View File
@@ -63,10 +63,42 @@ abstract class StructuredData
$siteName = static::siteName();
$node = [
'@type' => 'Organization',
'@type' => 'ProfessionalService',
'@id' => $siteUrl . '#organization',
'name' => $siteName,
'url' => $siteUrl,
'priceRange' => '$$',
'geo' => [
'@type' => 'GeoCoordinates',
'latitude' => 37.025704,
'longitude' => 37.296559,
],
'areaServed' => [
[
'@type' => 'AdministrativeArea',
'name' => 'Gaziantep',
],
[
'@type' => 'Country',
'name' => 'Turkey',
]
],
'knowsAbout' => [
'Yazılım Geliştirme',
'Web Tasarım',
'Mobil Uygulama Geliştirme',
'E-Ticaret Sistemleri',
'SEO Danışmanlığı',
'Gaziantep Yazılım Şirketleri'
],
'openingHoursSpecification' => [
[
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
'opens' => '09:00',
'closes' => '18:00',
]
]
];
$logo = setting('site_logo');
@@ -93,6 +125,10 @@ abstract class StructuredData
$node['address'] = [
'@type' => 'PostalAddress',
'streetAddress' => $address,
'addressLocality' => 'Şahinbey',
'addressRegion' => 'Gaziantep',
'postalCode' => '27190',
'addressCountry' => 'TR',
];
}
+91
View File
@@ -128,5 +128,96 @@ return [
'ar' => 'يمكنكم إضافة حساباتكم البنكية من لوحة الإدارة.',
'ru' => 'Вы можете добавить банковские счета из панели администратора.',
],
'Künye' => [
'en' => 'Imprint',
'de' => 'Impressum',
'se' => 'Imprint',
'ar' => 'البيانات القانونية',
'ru' => 'Юридическая информация',
],
'Resmi şirket bilgileri ve yasal sorumluluklar.' => [
'en' => 'Official company information and legal responsibilities.',
'de' => 'Offizielle Firmeninformationen und rechtliche Verantwortung.',
'se' => 'Officiell företagsinformation och juridiskt ansvar.',
'ar' => 'معلومات الشركة الرسمية والمسؤوليات القانونية.',
'ru' => 'Официальная информация о компании и юридическая ответственность.',
],
'Yasal Künye Bilgileri' => [
'en' => 'Legal Imprint Details',
'de' => 'Rechtliche Angaben (Impressum)',
'se' => 'Juridisk information',
'ar' => 'تفاصيل البيانات القانونية',
'ru' => 'Юридические выходные данные',
],
'Ticaret Sicil No' => [
'en' => 'Trade Registry No',
'de' => 'Handelsregisternummer',
'se' => 'Organisationsnummer',
'ar' => 'رقم السجل التجاري',
'ru' => 'Регистрационный номер компании',
],
'Ticaret Odası' => [
'en' => 'Chamber of Commerce',
'de' => 'Handelskammer',
'se' => 'Handelskammare',
'ar' => 'الغرفة التجارية',
'ru' => 'Торгово-промышленная палата',
],
'Gaziantep Ticaret Odası (GTO)' => [
'en' => 'Gaziantep Chamber of Commerce (GTO)',
'de' => 'Handelskammer Gaziantep (GTO)',
'se' => 'Gazianteps Handelskammare (GTO)',
'ar' => 'غرفة تجارة غازي عنتاب (GTO)',
'ru' => 'Торгово-промышленная палата Газиантепа (GTO)',
],
'Sorumlu Müdür' => [
'en' => 'Responsible Manager',
'de' => 'Verantwortlicher Geschäftsführer',
'se' => 'Ansvarig utgivare',
'ar' => 'المدير المسؤول',
'ru' => 'Ответственный менеджер',
],
'Yer Sağlayıcı' => [
'en' => 'Hosting Provider',
'de' => 'Hosting-Anbieter',
'se' => 'Webbhotell',
'ar' => 'مزود خدمة الاستضافة',
'ru' => 'Хостинг-провайдер',
],
'KEP Adresi' => [
'en' => 'Registered Email (KEP)',
'de' => 'Registrierte E-Mail (KEP)',
'se' => 'Registrerad e-post (KEP)',
'ar' => 'البريد الإلكتروني المسجل (KEP)',
'ru' => 'Зарегистрированный E-mail (KEP)',
],
'İletişim ve Resmi Bilgiler' => [
'en' => 'Contact & Official Info',
'de' => 'Kontakt & Offizielle Informationen',
'se' => 'Kontakt & Officiell information',
'ar' => 'الاتصال والمعلومات الرسمية',
'ru' => 'Контактная и официальная информация',
],
'Kurumsal Bilgiler' => [
'en' => 'Corporate Information',
'de' => 'Unternehmensinformationen',
'se' => 'Företagsinformation',
'ar' => 'معلومات الشركة',
'ru' => 'Информация о компании',
],
'Yetkili Temsilci' => [
'en' => 'Authorized Representative',
'de' => 'Vertretungsberechtigte Person',
'se' => 'Behörig företrädare',
'ar' => 'الممثل المفوض',
'ru' => 'Уполномоченный представитель',
],
'Telefon' => [
'en' => 'Phone',
'de' => 'Telefon',
'se' => 'Telefon',
'ar' => 'الهاتف',
'ru' => 'Телефон',
],
],
];
@@ -0,0 +1,28 @@
<?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::table('career_applications', function (Blueprint $table) {
$table->string('id_photocopy_path')->nullable()->after('contract_path');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('career_applications', function (Blueprint $table) {
$table->dropColumn('id_photocopy_path');
});
}
};
@@ -0,0 +1,28 @@
<?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::table('career_applications', function (Blueprint $table) {
$table->string('github_repo')->nullable()->after('internship_total_days');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('career_applications', function (Blueprint $table) {
$table->dropColumn('github_repo');
});
}
};
@@ -0,0 +1,29 @@
<?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::table('career_applications', function (Blueprint $table) {
$table->string('certificate_code')->nullable()->unique();
$table->text('transcript_markdown')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('career_applications', function (Blueprint $table) {
$table->dropColumn(['certificate_code', 'transcript_markdown']);
});
}
};
@@ -0,0 +1,29 @@
<?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::table('internship_journal_entries', function (Blueprint $table) {
$table->boolean('supervisor_approved')->default(false);
$table->boolean('unit_approved')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('internship_journal_entries', function (Blueprint $table) {
$table->dropColumn(['supervisor_approved', 'unit_approved']);
});
}
};
@@ -0,0 +1,36 @@
<?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('internship_journal_entries', function (Blueprint $table) {
$table->id();
$table->foreignId('career_application_id')
->constrained('career_applications')
->onDelete('cascade');
$table->integer('day_number');
$table->date('date');
$table->text('content')->nullable();
$table->timestamps();
$table->unique(['career_application_id', 'day_number'], 'journal_app_day_unique');
$table->unique(['career_application_id', 'date'], 'journal_app_date_unique');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('internship_journal_entries');
}
};
@@ -0,0 +1,38 @@
<?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::table('career_applications', function (Blueprint $table) {
$table->boolean('notebook_supervisor_signed')->default(false);
$table->string('notebook_supervisor_name')->nullable();
$table->boolean('notebook_unit_signed')->default(false);
$table->string('notebook_unit_name')->nullable();
$table->boolean('notebook_approved')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('career_applications', function (Blueprint $table) {
$table->dropColumn([
'notebook_supervisor_signed',
'notebook_supervisor_name',
'notebook_unit_signed',
'notebook_unit_name',
'notebook_approved'
]);
});
}
};
@@ -0,0 +1,28 @@
<?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::table('internship_journal_entries', function (Blueprint $table) {
$table->boolean('is_retroactive')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('internship_journal_entries', function (Blueprint $table) {
$table->dropColumn('is_retroactive');
});
}
};
@@ -0,0 +1,28 @@
<?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::table('internship_journal_entries', function (Blueprint $table) {
$table->string('supervisor_name')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('internship_journal_entries', function (Blueprint $table) {
$table->dropColumn('supervisor_name');
});
}
};
@@ -0,0 +1,28 @@
<?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::table('career_applications', function (Blueprint $table) {
$table->string('github_username')->nullable()->after('github_repo');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('career_applications', function (Blueprint $table) {
$table->dropColumn('github_username');
});
}
};
+54
View File
@@ -76,4 +76,58 @@ return [
'rejected' => 'مرفوض',
'accepted' => 'مقبول',
'waiting_document' => 'في انتظار الوثيقة',
// Wizard and Mermaid
'step_1_title' => '1. الشروط والأحكام',
'step_2_title' => '2. تحميل النماذج',
'step_3_title' => '3. أكمل التطبيق',
'kvkk_title' => 'الغرض من جمع المستندات (معلومات KVKK)',
'kvkk_nda_title' => '1. اتفاقية السرية (NDA)',
'kvkk_nda_desc' => 'مطلوب لضمان أمن رموز المصدر لمشروعنا ومخططات قاعدة البيانات والأسرار التجارية.',
'kvkk_contract_title' => '2. اتفاقية مقاول مستقل',
'kvkk_contract_desc' => 'مطلوب لتأمين شروط وحقوق نموذج العمل المرن القائم على المشكلات/المهام قانونيًا.',
'kvkk_id_title' => '3. نسخة من الهوية',
'kvkk_id_desc' => 'مطلوب للتحقق من الهوية القانونية، وتحديد المفوض بالتوقيع، ومنع الطلبات الاحتيالية.',
'hr_flow_diagram' => 'مخطط تقدم الموارد البشرية',
'terms_accept_label' => 'لقد قرأت نموذج العمل وشروط التقديم ومخطط تدفق عملية الموارد البشرية وأوافق عليها.',
'accept_terms_btn' => 'قبول الشروط والاستمرار',
'download_required_documents' => 'تحميل المستندات المطلوبة',
'download_required_documents_desc' => 'لإكمال طلبك، يجب عليك تحميل الاتفاقيات القانونية التالية، وقراءتها بعناية، وتوقيعها، ثم تحميلها في الخطوة التالية.',
'nda_card_desc' => 'إنها اتفاقية عدم الإفشاء التي يجب توقيعها لحماية سرية الرموز البرمجية للبرامج والبيانات والعمليات التابعة لمشاريع شركة Trunçgil Teknoloji.',
'review_download_document' => 'مراجعة وتحميل الاتفاقية',
'contract_card_desc' => 'إنها اتفاقية خدمات المقاول المستقل التي تحدد شروط العمل عن بعد القائم على أساس المشاكل/المهام، وشروط الدفع، وحقوق الأطراف.',
'go_back' => 'الرجوع',
'downloaded_next_btn' => 'لقد قمت بتحميل الاتفاقيات، انتقل إلى الخطوة الأخيرة',
'name_placeholder' => 'مثال: أحمد يلمظ',
'email_placeholder' => 'مثال: ahmet@example.com',
'phone_placeholder' => 'مثال: 05XX XXX XX XX',
'id_photocopy' => 'نسخة من الهوية',
'id_photocopy_description' => 'نسخة من البطاقة الشخصية من الجهتين أو بطاقة الهوية الجديدة أو رخصة القيادة (PDF, JPG, PNG - بحد أقصى 5 ميجابايت)',
// Mermaid Nodes
'mermaid_start' => 'طلب واتفاقية عدم الإفشاء',
'mermaid_dec1' => 'هل تمت الموافقة على المرشح؟',
'mermaid_red1' => 'رفض - تنتهي العملية',
'mermaid_git' => 'يتم توفير الوصول إلى مستودع GitHub',
'mermaid_sample' => 'يقوم المرشح بتثبيت المشروع ويعمل على مهمة عينة',
'mermaid_dec2' => 'هل تم إكمال مهمة العينة بنجاح؟',
'mermaid_red2' => 'رفض - تنتهي العملية',
'mermaid_issues' => 'الاستمرار في المشاكل الأخرى',
'mermaid_dec3' => 'هل اختار المرشح مشكلة؟',
'mermaid_wait' => 'المرشح في الانتظار أو يختار مهمة أخرى',
'mermaid_assign' => 'يتم تعيين المشكلة',
'mermaid_work' => 'يبدأ العمل',
'mermaid_review' => 'التسليم والمراجعة',
'mermaid_dec4' => 'هل تمت الموافقة على المهمة؟',
'mermaid_feedback' => 'طلب تصحيح وتعليقات',
'mermaid_payment' => 'يتم دفع الرسوم',
// Internship Wizard keys
'dear_intern' => 'عزيزي المرشح للمتدرب،',
'intern_welcome_text_1' => 'بصفتنا Trunçgil Technology، نحن فخورون بالمساهمة في تطوير المواهب الشابة من خلال برنامج التدريب عن بعد. نحن نقيّم المتدربين لدينا في مشاريع حقيقية، ونوفر لهم تجربة فريدة من نوعها في العمل عن بعد وتنسيق الفريق.',
'intern_welcome_text_2' => 'لكي يتم تقييم طلبك، يجب عليك أولاً مراجعة نموذج العمل وشروط التقديم لدينا، ثم ملء النموذج بالكامل عن طريق قبول شروط طلب التدريب.',
'intern_terms_accept_label' => 'لقد قرأت نموذج عمل التدريب عن بعد وشروط التقديم وتدفق عملية الموارد البشرية وأوافق عليها.',
'accept_intern_terms_btn' => 'قبول الشروط والانتقال إلى استمارة الطلب',
'intern_step_1_title' => '1. الشروط والأحكام',
'intern_step_2_title' => '2. أكمل التطبيق',
];
+144
View File
@@ -0,0 +1,144 @@
<?php
return [
// Navigation & list
'nav-music-productions' => 'الإنتاج الموسيقي',
'breadcrumb_home' => 'الرئيسية',
'meta-index-subtitle' => 'Truncgil',
'meta-index-title' => 'إنتاج موسيقي، موسيقى أفلام ومشاريع موسيقية مؤسسية.',
'meta-index-description' => 'اكتشف ألبوماتنا وسينغلاتنا ومشاريع الإنتاج المنشورة على Spotify وYouTube والمنصات الرقمية.',
'empty' => 'لا يوجد إنتاج موسيقي مسجّل حتى الآن.',
'search_placeholder' => 'ابحث بالعنوان أو اسم العميل…',
'search_no_results' => 'لا توجد نتائج لـ ":query".',
'card_category_default' => 'إنتاج',
'production_date_unknown' => 'التاريخ غير محدد',
'view_details_tooltip' => 'عرض التفاصيل',
'project_details' => 'تفاصيل المشروع',
'back_to_list' => 'العودة إلى القائمة',
'prev' => 'السابق',
'next' => 'التالي',
// Module labels
'title' => 'الإنتاج الموسيقي',
'navigation_label' => 'إنتاج موسيقي',
'model_label' => 'إنتاج موسيقي',
'plural_model_label' => 'إنتاجات موسيقية',
// Actions
'create' => 'إنشاء إنتاج موسيقي جديد',
'edit' => 'تعديل الإنتاج الموسيقي',
'save' => 'حفظ',
'cancel' => 'إلغاء',
'delete' => 'حذف الإنتاج الموسيقي',
'restore' => 'استعادة الإنتاج الموسيقي',
'force_delete' => 'حذف نهائي',
// Form fields
'title_field' => 'العنوان',
'slug_field' => 'مسار URL',
'content_field' => 'المحتوى',
'client_name_field' => 'اسم العميل',
'production_date_field' => 'تاريخ الإنتاج',
'cover_image_field' => 'صورة الغلاف',
'gallery_field' => 'صور المعرض',
'is_active_field' => 'نشط؟',
'sort_order_field' => 'ترتيب الفرز',
// Sections
'content_section' => 'المحتوى',
'settings_section' => 'إعدادات الإنتاج',
'translations_section' => 'الترجمات',
// Helper texts
'slug_helper' => 'الجزء الذي سيظهر في URL. مثال: music-production-1',
'cover_image_helper' => 'اختر صورة غلاف للإنتاج',
'gallery_helper' => 'اختر صورًا أخرى للإنتاج (يدعم الرفع المتعدد)',
'production_date_helper' => 'تاريخ اكتمال الإنتاج',
'is_active_helper' => 'عرض هذا الإنتاج الموسيقي على الموقع',
'sort_order_helper' => 'أدخل رقم ترتيب لأولوية القائمة',
// Status options
'status_draft' => 'مسودة',
'status_published' => 'منشور',
'status_archived' => 'مؤرشف',
// Messages
'created_successfully' => 'تم إنشاء الإنتاج الموسيقي بنجاح.',
'updated_successfully' => 'تم تحديث الإنتاج الموسيقي بنجاح.',
'deleted_successfully' => 'تم حذف الإنتاج الموسيقي بنجاح.',
'restored_successfully' => 'تم استعادة الإنتاج الموسيقي بنجاح.',
// Table columns
'table_title' => 'العنوان',
'table_slug' => 'مسار URL',
'table_client_name' => 'العميل',
'table_production_date' => 'تاريخ الإنتاج',
'table_status' => 'الحالة',
'table_is_active' => 'نشط',
'table_created_at' => 'تاريخ الإنشاء',
'table_updated_at' => 'تاريخ التحديث',
// Validation messages
'title_required' => 'حقل العنوان إلزامي.',
'slug_required' => 'حقل مسار URL إلزامي.',
'slug_unique' => 'هذا المسار مستخدم بالفعل.',
// Spotify
'spotify_section' => 'معلومات Spotify',
'spotify_album_id_field' => 'معرّف ألبوم Spotify',
'spotify_url_field' => 'رابط Spotify',
'spotify_type_field' => 'نوع Spotify',
'spotify_synced_at_field' => 'آخر مزامنة Spotify',
'spotify_type_album' => 'ألبوم',
'spotify_type_single' => 'سينغل',
'spotify_type_compilation' => 'تجميعة',
'spotify_listen_on_spotify' => 'استمع على Spotify',
'spotify_track_singular' => 'مقطوعة',
'spotify_track_plural' => 'مقطوعات',
'sync_spotify' => 'مزامنة من Spotify',
'sync_spotify_heading' => 'مزامنة Spotify',
'sync_spotify_description' => 'سيتم مزامنة الألبومات والسينغلات من حساب الفنان مع الإنتاجات الموسيقية.',
'spotify_sync_started' => 'جارٍ بدء مزامنة Spotify...',
'spotify_sync_completed' => 'اكتملت المزامنة. :created جديد، :updated محدَّث (الإجمالي :total).',
'spotify_sync_success' => 'اكتملت مزامنة Spotify بنجاح.',
'spotify_sync_failed' => 'فشلت مزامنة Spotify. تحقق من السجلات.',
'spotify_not_configured' => 'بيانات Spotify API مفقودة. حدد SPOTIFY_CLIENT_ID وSPOTIFY_CLIENT_SECRET وSPOTIFY_ARTIST_ID في ملف .env.',
'spotify_token_failed' => 'فشل الحصول على رمز الوصول لـ Spotify.',
'spotify_albums_fetch_failed' => 'فشل جلب قائمة ألبومات Spotify.',
'spotify_premium_required' => 'يحتاج حساب Spotify المطوّر إلى اشتراك Premium نشط. Detail: :detail',
'spotify_no_albums' => 'لم يتم العثور على ألبومات أو سينغلات في حساب فنان Spotify.',
'spotify_untitled_album' => 'ألبوم بلا عنوان',
// YouTube
'youtube_section' => 'معلومات YouTube',
'youtube_video_id_field' => 'معرّف فيديو YouTube',
'youtube_url_field' => 'رابط YouTube',
'youtube_description_field' => 'وصف YouTube',
'youtube_synced_at_field' => 'آخر مزامنة YouTube',
'youtube_watch_on_youtube' => 'شاهد على YouTube',
'sync_youtube' => 'مزامنة من YouTube',
'sync_youtube_heading' => 'مزامنة إصدارات YouTube',
'sync_youtube_description' => 'سيتم مزامنة إصدارات المقاطع الفنية من قائمة تشغيل Releases في قناتك.',
'youtube_sync_started' => 'جارٍ بدء مزامنة إصدارات YouTube...',
'youtube_sync_source' => 'المصدر: :source',
'youtube_sync_completed' => 'اكتملت المزامنة. :created جديد، :updated محدَّث (الإجمالي :total).',
'youtube_sync_success' => 'اكتملت مزامنة YouTube بنجاح.',
'youtube_sync_failed' => 'فشلت مزامنة YouTube. تحقق من السجلات.',
'youtube_not_configured' => 'بيانات YouTube API مفقودة. حدد YOUTUBE_API_KEY وYOUTUBE_CHANNEL_ID في ملف .env.',
'youtube_releases_fetch_failed' => 'فشل جلب قائمة إصدارات YouTube.',
'youtube_referrer_blocked' => 'رفض مفتاح YouTube API الطلب. Detail: :detail',
'youtube_playlist_not_resolved' => 'تعذّر تحليل معرّف قائمة التشغيل. حدد YOUTUBE_CHANNEL_ID أو YOUTUBE_PLAYLIST_ID.',
'youtube_no_releases' => 'لم يتم العثور على إصدارات في قائمة تشغيل YouTube Releases.',
'youtube_untitled_release' => 'إصدار بلا عنوان',
// Metadata table labels
'field' => 'الحقل',
'value' => 'القيمة',
'track' => 'المقطوعة',
'style' => 'الأسلوب',
'contributors' => 'المساهمون',
'released_on' => 'تاريخ الإصدار',
'distributor' => 'الموزع',
'label' => 'التسجيلات',
'youtube' => 'YouTube',
];
+54
View File
@@ -85,4 +85,58 @@ return [
'rejected' => 'Abgelehnt',
'accepted' => 'Akzeptiert',
'waiting_document' => 'Warten auf Dokument',
// Wizard and Mermaid
'step_1_title' => '1. Geschäftsbedingungen',
'step_2_title' => '2. Formulare herunterladen',
'step_3_title' => '3. Bewerbung abschließen',
'kvkk_title' => 'Zweck der Dokumentensammlung (KVKK-Information)',
'kvkk_nda_title' => '1. Vertraulichkeitsvereinbarung (NDA)',
'kvkk_nda_desc' => 'Erforderlich, um die Sicherheit der Quellcodes, Datenbankschemata und Geschäftsgeheimnisse unseres Projekts zu gewährleisten.',
'kvkk_contract_title' => '2. Freier Mitarbeitervertrag',
'kvkk_contract_desc' => 'Erforderlich, um die Bedingungen und Ihre Rechte des aufgabenbasierten flexiblen Arbeitsmodells rechtlich abzusichern.',
'kvkk_id_title' => '3. Ausweiskopie',
'kvkk_id_desc' => 'Erforderlich zur gesetzlichen Identitätsprüfung, Ermittlung des zeichnungsberechtigten Vertreters und zur Vermeidung betrügerischer Bewerbungen.',
'hr_flow_diagram' => 'HR-Fortschrittsdiagramm',
'terms_accept_label' => 'Ich habe das Arbeitsmodell, die Bewerbungsbedingungen und das HR-Prozessdiagramm gelesen und akzeptiere sie.',
'accept_terms_btn' => 'Bedingungen akzeptieren und fortfahren',
'download_required_documents' => 'Erforderliche Dokumente herunterladen',
'download_required_documents_desc' => 'Um Ihre Bewerbung abzuschließen, müssen Sie die folgenden rechtlichen Vereinbarungen herunterladen, sorgfältig lesen, unterschreiben und im nächsten Schritt hochladen.',
'nda_card_desc' => 'Es handelt sich um die Vertraulichkeitsvereinbarung (NDA), die zwingend zu unterzeichnen ist, um die Vertraulichkeit von Quellcodes, Daten und Prozessen von Trunçgil Technology-Projekten zu schützen.',
'review_download_document' => 'Vereinbarung prüfen & herunterladen',
'contract_card_desc' => 'Es handelt sich um den Dienstvertrag für freie Mitarbeiter, der die Bedingungen für die aufgabenbasierte Remote-Arbeit, die Zahlungsbedingungen und die Rechte der Parteien regelt.',
'go_back' => 'Zurückgehen',
'downloaded_next_btn' => 'Ich habe die Vereinbarungen heruntergeladen, zum letzten Schritt fortfahren',
'name_placeholder' => 'z. B.: Max Mustermann',
'email_placeholder' => 'z. B.: max@example.com',
'phone_placeholder' => 'z. B.: +49 1XX XXX XX XX',
'id_photocopy' => 'Ausweiskopie',
'id_photocopy_description' => 'Vorder- und Rückseite des Personalausweises, der neuen Identitätskarte oder des Führerscheins (PDF, JPG, PNG - Max. 5MB)',
// Mermaid Nodes
'mermaid_start' => 'Bewerbung und Vertraulichkeitsvereinbarung',
'mermaid_dec1' => 'Bewerber freigegeben?',
'mermaid_red1' => 'Ablehnung - Prozess endet',
'mermaid_git' => 'GitHub-Repository-Zugriff gewährt',
'mermaid_sample' => 'Bewerber richtet Projekt ein und arbeitet an einer Beispielaufgabe',
'mermaid_dec2' => 'Beispielaufgabe erfolgreich abgeschlossen?',
'mermaid_red2' => 'Ablehnung - Prozess endet',
'mermaid_issues' => 'Weiter mit anderen Issues',
'mermaid_dec3' => 'Hat der Bewerber ein Issue ausgewählt?',
'mermaid_wait' => 'Bewerber wartet oder wählt andere Aufgabe',
'mermaid_assign' => 'Issue-Zuweisung erfolgt',
'mermaid_work' => 'Arbeit beginnt',
'mermaid_review' => 'Abgabe und Überprüfung',
'mermaid_dec4' => 'Aufgabe freigegeben?',
'mermaid_feedback' => 'Korrekturanforderung und Feedback',
'mermaid_payment' => 'Zahlung wird geleistet',
// Internship Wizard keys
'dear_intern' => 'Sehr geehrter Praktikantenkandidat,',
'intern_welcome_text_1' => 'Als Trunçgil Technology sind wir stolz darauf, durch unser Remote-Praktikumsprogramm zur Entwicklung junger Talente beizutragen. Wir bewerten unsere Praktikanten in realen Projekten und bieten ihnen eine einzigartige Erfahrung in der Remote-Arbeit und Teamkoordination.',
'intern_welcome_text_2' => 'Damit Ihre Bewerbung bewertet werden kann, müssen Sie zunächst unser Arbeitsmodell und unsere Bewerbungsbedingungen prüfen und dann das Formular durch Akzeptieren der Praktikumsbewerbungsbedingungen vollständig ausfüllen.',
'intern_terms_accept_label' => 'Ich habe das Arbeitsmodell des Remote-Praktikums, die Bewerbungsbedingungen und den HR-Prozessablauf gelesen und akzeptiere sie.',
'accept_intern_terms_btn' => 'Bedingungen akzeptieren und zum Bewerbungsformular fortfahren',
'intern_step_1_title' => '1. Geschäftsbedingungen',
'intern_step_2_title' => '2. Bewerbung abschließen',
];
+15
View File
@@ -0,0 +1,15 @@
<?php
return [
// Metadata table labels
'field' => 'Feld',
'value' => 'Wert',
'track' => 'Titel',
'style' => 'Stil',
'contributors' => 'Mitwirkende',
'released_on' => 'Veröffentlicht am',
'distributor' => 'Vertriebsfirma',
'label' => 'Label',
'youtube' => 'YouTube',
];
?>
+77 -1
View File
@@ -70,7 +70,7 @@ return [
'remote_work_title' => 'Fully Remote Work',
'remote_work_details' => 'Team coordination is maintained through daily standup meetings. Working hours are more flexible in some projects. Interns are evaluated in one or more real projects suitable for their CV and skills.',
'application_conditions_title' => 'Application Conditions',
'condition_not_only_for_internship' => 'Applications from individuals who only wish to do an internship are not accepted. Candidates who are open to development and aim to contribute are our priority.',
'condition_not_only_for_internship' => 'Our internship program prioritizes dynamic team members who are open to continuous development, interested in innovation, and aim to actively contribute to projects, rather than candidates who only wish to fulfill academic requirements.',
'condition_intent_letter' => 'In the message section, you must provide a letter of intent stating how you will contribute to our projects.',
'daily_standup' => 'Daily Standup',
'daily_standup_desc' => 'Daily short meetings ensure in-team communication and progress tracking.',
@@ -85,4 +85,80 @@ return [
'rejected' => 'Rejected',
'accepted' => 'Accepted',
'waiting_document' => 'Waiting for Document',
// Wizard and Mermaid
'step_1_title' => '1. Terms & Conditions',
'step_2_title' => '2. Download Forms',
'step_3_title' => '3. Complete Application',
'kvkk_title' => 'Purpose of Document Collection (KVKK Information)',
'kvkk_nda_title' => '1. Non-Disclosure Agreement (NDA)',
'kvkk_nda_desc' => 'Requested to ensure the security of our project\'s source codes, database schemas, and trade secrets.',
'kvkk_contract_title' => '2. Independent Contractor Agreement',
'kvkk_contract_desc' => 'Required to legally secure the conditions and your rights of the issue/task-based flexible working model.',
'kvkk_id_title' => '3. Identity Photocopy',
'kvkk_id_desc' => 'Requested for legal identity verification, determination of authorized signatory, and prevention of fraudulent applications.',
'hr_flow_diagram' => 'HR Progress Diagram',
'terms_accept_label' => 'I have read and accept the working model, application conditions, and the HR process flowchart.',
'accept_terms_btn' => 'Accept Terms and Proceed',
'download_required_documents' => 'Download Required Documents',
'download_required_documents_desc' => 'To complete your application, you must download the following legal agreements, read them carefully, sign them, and upload them in the next step.',
'nda_card_desc' => 'It is the Non-Disclosure Agreement that is mandatory to sign in order to protect the confidentiality of source codes, data, and processes belonging to Trunçgil Technology projects.',
'review_download_document' => 'Review & Download Agreement',
'contract_card_desc' => 'It is the Independent Contractor Services Agreement that defines the conditions of job/issue-based remote work, payment terms, and rights of the parties.',
'go_back' => 'Go Back',
'downloaded_next_btn' => 'I Have Downloaded the Agreements, Proceed to the Last Step',
'name_placeholder' => 'e.g.: John Doe',
'email_placeholder' => 'e.g.: john@example.com',
'phone_placeholder' => 'e.g.: +90 5XX XXX XX XX',
'id_photocopy' => 'Identity Photocopy',
'id_photocopy_description' => 'Front and back photocopy of national ID, new identity card, or driver\'s license (PDF, JPG, PNG - Max. 5MB)',
// Mermaid Nodes
'mermaid_start' => 'Application and Non-Disclosure Agreement',
'mermaid_dec1' => 'Is Candidate Approved?',
'mermaid_red1' => 'Rejection - Process Ends',
'mermaid_git' => 'GitHub Repo Access Provided',
'mermaid_sample' => 'Candidate Sets Up Project and Works on Sample Task',
'mermaid_dec2' => 'Sample Task Successfully Completed?',
'mermaid_red2' => 'Rejection - Process Ends',
'mermaid_issues' => 'Continue to Other Issues',
'mermaid_dec3' => 'Did Candidate Select an Issue?',
'mermaid_wait' => 'Candidate Waiting or Selects Another Task',
'mermaid_assign' => 'Issue Assignment is Done',
'mermaid_work' => 'Work Starts',
'mermaid_review' => 'Submission and Review',
'mermaid_dec4' => 'Is Task Approved?',
'mermaid_feedback' => 'Revision Request and Feedback',
'mermaid_payment' => 'Fee Payment is Made',
// Internship Wizard keys
'dear_intern' => 'Dear Intern Candidate,',
'intern_welcome_text_1' => 'As Trunçgil Technology, we are proud to contribute to the development of young talents through our remote internship program. We evaluate our interns in real projects, providing them with a unique remote working and team coordination experience.',
'intern_welcome_text_2' => 'In order for your application to be evaluated, you must first review our working model and application conditions, and then fill out the form completely by accepting the internship application terms.',
'intern_terms_accept_label' => 'I have read and accept the remote internship working model, application conditions, and the HR process flow.',
'accept_intern_terms_btn' => 'Accept Terms and Proceed to Application Form',
'intern_step_1_title' => '1. Terms & Conditions',
'intern_step_2_title' => '2. Complete Application',
'intern_flow_diagram' => 'Intern Admission and Working System Diagram',
// Intern Mermaid Nodes
'intern_mermaid_start' => 'Internship Application & Document Submission',
'intern_mermaid_dec1' => 'Is Onboarding Evaluation Eligible?',
'intern_mermaid_red1' => 'Eliminated - Process Ends',
'intern_mermaid_doc_upload' => 'Upload University Internship Form',
'intern_mermaid_doc_sign' => 'Form Signed by Institution & Downloaded',
'intern_mermaid_git' => 'Process Orientation Starts',
'intern_mermaid_sample' => 'GIT & Technical Foundations Training',
'intern_mermaid_dec2' => 'Has GIT Knowledge?',
'intern_mermaid_red2' => 'Eliminated - Process Ends',
'intern_mermaid_real' => 'Involvement in Real Projects',
'intern_mermaid_dec3' => 'New Task / Issue Selection',
'intern_mermaid_wait' => 'Mentor Support & Development',
'intern_mermaid_assign' => 'Task Assignment & Coding',
'intern_mermaid_work' => 'Coding & Daily Standup',
'intern_mermaid_review' => 'Code Review (PR)',
'intern_mermaid_dec4' => 'PR Approved (Merged)?',
'intern_mermaid_feedback' => 'Revision Request (Feedback)',
'intern_mermaid_success' => 'Successful Task & Experience',
'intern_mermaid_graduation' => 'Trunçgil Signed Completion Certificate',
];
+12
View File
@@ -130,4 +130,16 @@ return [
'youtube_playlist_not_resolved' => 'Could not resolve YouTube Releases playlist ID. Set YOUTUBE_CHANNEL_ID or YOUTUBE_PLAYLIST_ID.',
'youtube_no_releases' => 'No releases found in the YouTube Releases playlist.',
'youtube_untitled_release' => 'Untitled Release',
// Metadata table labels
'field' => 'Field',
'value' => 'Value',
'track' => 'Track',
'style' => 'Style',
'contributors' => 'Contributors',
'released_on' => 'Released on',
'distributor' => 'Distributor',
'label' => 'Label',
'youtube' => 'YouTube',
];
+142
View File
@@ -0,0 +1,142 @@
<?php
return [
'navigation_label' => 'Заявки на вакансии',
'model_label' => 'Заявка',
'plural_model_label' => 'Заявки на вакансии',
'title' => 'Анкета кандидата',
'description' => 'Чтобы присоединиться к нашей команде, пожалуйста, заполните форму ниже и загрузите свое резюме.',
'job_application_title' => 'Подача заявки на работу',
'job_application_description' => 'Чтобы присоединиться к нашей команде, пожалуйста, заполните форму ниже и загрузите свое резюме.',
'internship_title' => 'Заявка на стажировку',
'internship_description' => 'Узнайте больше о нашей программе дистанционной стажировки и подайте заявку.',
'name' => 'Имя и фамилия',
'email' => 'Электронная почта',
'phone' => 'Телефон',
'cv' => 'Резюме (CV)',
'nda' => 'Подписанное NDA',
'contract' => 'Подписанный договор',
'git_knowledge' => 'Знание GIT',
'ai_usage' => 'Использование инструментов ИИ',
'message' => 'Сообщение',
'status' => 'Статус',
'type' => 'Тип заявки',
'job' => 'Работа',
'internship' => 'Стажировка',
'submit' => 'Отправить заявку',
'success_message' => 'Ваша заявка успешно получена. Спасибо.',
'download_cv' => 'Скачать резюме',
'created_at' => 'Дата подачи',
// Intro section
'dear_developer' => 'Уважаемый кандидат,',
'intro_text_1' => 'Как компания Trunçgil Technology, мы сотрудничаем с независимыми разработчиками всех уровней для разработки нашего программного проекта на базе Laravel. В качестве бизнес-модели применяется <strong>система оплаты на основе задач (issue/task)</strong>, при этом задачи предлагаются кандидатам с заранее определенной стоимостью.',
'intro_text_2' => 'Основная причина, по которой мы просим вас предоставить указанную ниже информацию, заключается в необходимости предоставления вам ограниченного доступа к исходному коду нашего проекта на этапе оценки вашей заявки. Поэтому в целях обеспечения юридической безопасности и защиты прав сторон требуется изучить указанные ниже документы: <strong>Соглашение о конфиденциальности (NDA)</strong> и <strong>Соглашение с независимым исполнителем</strong>, а также предоставить их подписанные копии при подаче заявки.',
'intro_text_3' => 'Пожалуйста, заполните форму ниже и загрузите подписанные документы в формате PDF единым файлом или zip-архивом.',
'intro_text_4' => 'Ваши данные будут использоваться конфиденциально исключительно для процесса рассмотрения заявки и юридической защиты интересов обеих сторон.',
'thanks' => 'Спасибо.',
'company_details' => 'TRUNÇGİL TECHNOLOGY INDUSTRY AND TRADE LIMITED COMPANY<br>Адрес: Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A Interior Door No: 1 Şahinbey / Gaziantep<br>Эл. почта: info@truncgil.com.tr<br>ИНН: 8591492435 – Şahinbey V.D. | MERSIS: 0859149243500001',
// Working model
'working_model_title' => 'Наша модель работы',
'working_model_subtitle' => 'Удаленная работа на основе задач',
'terms_title' => 'Правила и условия',
'fully_remote' => 'Полностью удаленно:',
'fully_remote_desc' => 'Вы можете выполнять задачи из любой точки мира в удобной для вас рабочей обстановке.',
'independent_contractor' => 'Независимый исполнитель:',
'independent_contractor_desc' => 'Вы вносите вклад в наши проекты на условиях фриланса.',
'mandatory_documents' => 'Обязательные документы:',
'mandatory_documents_desc' => 'По соображениям безопасности подписание NDA и Соглашения с независимым исполнителем обязательно.',
'nda_label' => 'Соглашение о конфиденциальности (NDA)',
'contract_label' => 'Соглашение с независимым исполнителем',
// Form section
'form_title' => 'Форма заявки',
'form_subtitle' => 'Пожалуйста, полностью заполните все поля.',
'nda_description' => 'Подписанное соглашение о конфиденциальности (PDF, макс. 5 МБ)',
'contract_description' => 'Подписанное соглашение с исполнителем (PDF, макс. 5 МБ)',
'git_confirmation' => 'Я умею пользоваться системой контроля версий GIT.',
'ai_confirmation' => 'Я активно использую инструменты ИИ (Copilot, ChatGPT и др.) в процессах разработки ПО.',
'message_placeholder' => 'Ваши навыки, опыт и то, что вы хотите добавить...',
'cv_description' => 'PDF, DOC, DOCX (макс. 5 МБ)',
// Messages
'success' => 'Успешно!',
'ok' => 'ОК',
'error' => 'Ошибка!',
'validation_error' => 'Пожалуйста, проверьте форму на наличие ошибок.',
'error_message' => 'Произошла ошибка при отправке заявки. Пожалуйста, попробуйте еще раз.',
'remote_work_title' => 'Полностью удаленная работа',
'remote_work_details' => 'Координация команды осуществляется через ежедневные летучки (standup). В некоторых проектах график более гибкий. Стажеры привлекаются к одному или нескольким реальным проектам, соответствующим их резюме и навыкам.',
'application_conditions_title' => 'Условия подачи заявки',
'condition_not_only_for_internship' => 'Заявки от лиц, желающих пройти стажировку исключительно «ради галочки», не принимаются. В приоритете кандидаты, нацеленные на развитие и готовые приносить пользу проекту.',
'condition_intent_letter' => 'В поле сообщения необходимо приложить сопроводительное письмо (сопроводительный текст) с описанием того, какой вклад вы планируете внести в наши проекты.',
'daily_standup' => 'Ежедневные летучки',
'daily_standup_desc' => 'Короткие ежедневные встречи обеспечивают коммуникацию внутри команды и отслеживание прогресса.',
'flexible_projects' => 'Гибкость и проектный подход',
'flexible_projects_desc' => 'В наших проектах важна гибкость. Вы будете работать над реальными задачами, соответствующими вашим компетенциям.',
'focus_on_development' => 'Ориентация на развитие',
'intent_letter' => 'Сопроводительное письмо',
// Statuses
'pending' => 'В ожидании',
'reviewed' => 'Рассмотрено',
'rejected' => 'Отклонено',
'accepted' => 'Принято',
'waiting_document' => 'Ожидание документов',
// Wizard and Mermaid
'step_1_title' => '1. Правила и условия',
'step_2_title' => '2. Скачать формы',
'step_3_title' => '3. Завершить подачу',
'kvkk_title' => 'Цель сбора документов (Информация в рамках KVKK)',
'kvkk_nda_title' => '1. Соглашение о конфиденциальности (NDA)',
'kvkk_nda_desc' => 'Запрашивается для обеспечения безопасности исходных кодов наших проектов, схем баз данных и коммерческих тайн.',
'kvkk_contract_title' => '2. Соглашение с независимым исполнителем',
'kvkk_contract_desc' => 'Необходимо для юридического закрепления условий и ваших прав в рамках гибкой модели работы на основе задач.',
'kvkk_id_title' => '3. Копия удостоверения личности',
'kvkk_id_desc' => 'Запрашивается для юридической проверки личности, определения полномочий подписи и предотвращения фиктивных заявок.',
'hr_flow_diagram' => 'Схема этапов подбора персонала',
'terms_accept_label' => 'Я ознакомился с моделью работы, условиями подачи заявки и схемой процесса подбора и принимаю их.',
'accept_terms_btn' => 'Принять условия и продолжить',
'download_required_documents' => 'Скачать необходимые документы',
'download_required_documents_desc' => 'Для завершения подачи заявки вам необходимо скачать следующие юридические соглашения, внимательно прочитать их, подписать и загрузить на следующем шаге.',
'nda_card_desc' => 'Это Соглашение о конфиденциальности (NDA), подписание которого обязательно для защиты конфиденциальности исходных кодов, данных и процессов, относящихся к проектам Trunçgil Technology.',
'review_download_document' => 'Просмотреть и скачать соглашение',
'contract_card_desc' => 'Это Соглашение об услугах независимого исполнителя, определяющее условия удаленной работы на основе задач, условия оплаты и права сторон.',
'go_back' => 'Назад',
'downloaded_next_btn' => 'Я скачал соглашения, перейти к последнему шагу',
'name_placeholder' => 'Например: Иван Иванов',
'email_placeholder' => 'Например: ivan@example.com',
'phone_placeholder' => 'Например: +7 9XX XXX XX XX',
'id_photocopy' => 'Копия удостоверения личности',
'id_photocopy_description' => 'Копия обеих сторон удостоверения личности, новой идентификационной карты или водительского удостоверения (PDF, JPG, PNG - макс. 5 МБ)',
// Mermaid Nodes
'mermaid_start' => 'Заявка и Соглашение о конфиденциальности',
'mermaid_dec1' => 'Кандидат утвержден?',
'mermaid_red1' => 'Отказ - Процесс завершается',
'mermaid_git' => 'Предоставляется доступ к репозиторию GitHub',
'mermaid_sample' => 'Кандидат настраивает проект и работает над тестовым заданием',
'mermaid_dec2' => 'Тестовое задание успешно выполнено?',
'mermaid_red2' => 'Отказ - Процесс завершается',
'mermaid_issues' => 'Переход к другим задачам',
'mermaid_dec3' => 'Кандидат выбрал задачу?',
'mermaid_wait' => 'Кандидат ожидает или выбирает другую задачу',
'mermaid_assign' => 'Производится назначение задачи',
'mermaid_work' => 'Начинается работа',
'mermaid_review' => 'Сдача и рассмотрение',
'mermaid_dec4' => 'Задача одобрена?',
'mermaid_feedback' => 'Запрос на доработку и обратная связь',
'mermaid_payment' => 'Производится выплата вознаграждения',
// Internship Wizard keys
'dear_intern' => 'Уважаемый кандидат на стажировку,',
'intern_welcome_text_1' => 'Как компания Trunçgil Technology, мы гордимся тем, что вносим вклад в развитие молодых талантов в рамках нашей программы дистанционной стажировки. Мы привлекаем стажеров к реальным проектам, давая им уникальный опыт удаленной работы и координации в команде.',
'intern_welcome_text_2' => 'Для оценки вашей заявки вам необходимо сначала ознакомиться с нашей моделью работы и условиями стажировки, а затем полностью заполнить форму, подтвердив согласие с условиями.',
'intern_terms_accept_label' => 'Я ознакомился с моделью дистанционной стажировки, условиями подачи заявки и этапами подбора персонала и принимаю их.',
'accept_intern_terms_btn' => 'Принять условия и перейти к анкете',
'intern_step_1_title' => '1. Правила и условия',
'intern_step_2_title' => '2. Завершить подачу',
];
+54
View File
@@ -85,4 +85,58 @@ return [
'rejected' => 'Avvisad',
'accepted' => 'Accepterad',
'waiting_document' => 'Väntar på dokument',
// Wizard and Mermaid
'step_1_title' => '1. Regler & Villkor',
'step_2_title' => '2. Ladda ner blanketter',
'step_3_title' => '3. Slutför ansökan',
'kvkk_title' => 'Syfte med dokumentinsamling (KVKK-information)',
'kvkk_nda_title' => '1. Sekretessavtal (NDA)',
'kvkk_nda_desc' => 'Krävs för att säkerställa säkerheten för våra projekts källkoder, databasscheman och affärshemligheter.',
'kvkk_contract_title' => '2. Avtal för oberoende uppdragstagare',
'kvkk_contract_desc' => 'Krävs för att juridiskt säkra villkoren och dina rättigheter för den uppdragsbaserade flexibla arbetsmodellen.',
'kvkk_id_title' => '3. Kopia på ID-handling',
'kvkk_id_desc' => 'Krävs för laglig identitetsverifiering, fastställande av behörig firmatecknare och för att förhindra bedrägliga ansökningar.',
'hr_flow_diagram' => 'HR Processdiagram',
'terms_accept_label' => 'Jag har läst och godkänner arbetsmodellen, ansökningsvillkoren och HR-processflödet.',
'accept_terms_btn' => 'Godkänn villkoren och fortsätt',
'download_required_documents' => 'Ladda ner nödvändiga dokument',
'download_required_documents_desc' => 'För att slutföra din ansökan måste du ladda ner följande juridiska avtal, läsa dem noggrant, signera dem och ladda upp dem i nästa steg.',
'nda_card_desc' => 'Detta är sekretessavtalet (NDA) som måste undertecknas för att skydda källkoder, data och processer som tillhör Trunçgil Teknologis projekt.',
'review_download_document' => 'Granska & ladda ner avtalet',
'contract_card_desc' => 'Detta är avtalet för oberoende uppdragstagare som definierar villkoren för den uppdragsbaserade distansarbetet, betalningsvillkoren och parternas rättigheter.',
'go_back' => 'Gå tillbaka',
'downloaded_next_btn' => 'Jag har laddat ner avtalen, gå till sista steget',
'name_placeholder' => 't.ex.: Sven Svensson',
'email_placeholder' => 't.ex.: sven@example.com',
'phone_placeholder' => 't.ex.: +46 7XX XXX XX XX',
'id_photocopy' => 'Kopia på ID-handling',
'id_photocopy_description' => 'Fram- och baksida på ID-kort, nytt identitetskort eller körkort (PDF, JPG, PNG - Max 5MB)',
// Mermaid Nodes
'mermaid_start' => 'Ansökan och sekretessavtal',
'mermaid_dec1' => 'Är kandidaten godkänd?',
'mermaid_red1' => 'Avslag - Processen avslutas',
'mermaid_git' => 'GitHub Repo-åtkomst beviljas',
'mermaid_sample' => 'Kandidaten sätter upp projektet och arbetar på en exempeluppgift',
'mermaid_dec2' => 'Är exempeluppgiften framgångsrikt slutförd?',
'mermaid_red2' => 'Avslag - Processen avslutas',
'mermaid_issues' => 'Fortsätt till andra uppdrag',
'mermaid_dec3' => 'Har kandidaten valt ett uppdrag?',
'mermaid_wait' => 'Kandidaten väntar eller väljer ett annat uppdrag',
'mermaid_assign' => 'Uppdraget tilldelas',
'mermaid_work' => 'Arbetet påbörjas',
'mermaid_review' => 'Inlämning och granskning',
'mermaid_dec4' => 'Är uppdraget godkänt?',
'mermaid_feedback' => 'Begäran om korrigering och feedback',
'mermaid_payment' => 'Arvode betalas ut',
// Internship Wizard keys
'dear_intern' => 'Bästa praktikantkandidat,',
'intern_welcome_text_1' => 'Som Trunçgil Technology är vi stolta över att bidra till utvecklingen av unga talanger genom vårt distanspraktikprogram. Vi utvärderar våra praktikanter i verkliga projekt och ger dem en unik erfarenhet av distansarbete och teamkoordinering.',
'intern_welcome_text_2' => 'För att din ansökan ska kunna utvärderas måste du först granska vår arbetsmodell och ansökningsvillkor, och sedan fylla i formuläret fullständigt genom att acceptera villkoren för praktikansökan.',
'intern_terms_accept_label' => 'Jag har läst och godkänner arbetsmodellen för distanspraktik, ansökningsvillkoren och HR-processflödet.',
'accept_intern_terms_btn' => 'Godkänn villkoren och gå till ansökningsformuläret',
'intern_step_1_title' => '1. Regler & Villkor',
'intern_step_2_title' => '2. Slutför ansökan',
];
+78 -2
View File
@@ -70,7 +70,7 @@ return [
'remote_work_title' => 'Tamamen Uzaktan Çalışma',
'remote_work_details' => 'Günlük standup toplantıları ile ekip koordinasyonu sağlanır. Bazı projelerde çalışma saatleri daha esnektir. Stajyerler, CV\'lerine ve yetkinliklerine uygun olan bir veya birden fazla gerçek projede değerlendirilir.',
'application_conditions_title' => 'Başvuru Şartları',
'condition_not_only_for_internship' => 'Yalnızca staj yapmak için yapmak isteyen kişiler alınmamaktadır. Gelişime açık ve katkı sağlamayı hedefleyen adaylar önceliğimizdir.',
'condition_not_only_for_internship' => 'Staj programımız, yalnızca akademik zorunlulukları yerine getirmek isteyen adaylardan ziyade; sürekli gelişime açık, inovasyona ilgi duyan ve projelere aktif katkı sağlamayı hedefleyen dinamik takım arkadaşlarına öncelik tanımaktadır.',
'condition_intent_letter' => 'Mesaj bölümünde projelerimize nasıl bir katkı sağlayacağınızı belirten bir niyet mektubu iletmeniz gerekmektedir.',
'daily_standup' => 'Günlük Standup',
'daily_standup_desc' => 'Her gün düzenlenen kısa toplantılarla ekip içi iletişim ve ilerleme takibi sağlanır.',
@@ -84,5 +84,81 @@ return [
'reviewed' => 'İncelendi',
'rejected' => 'Reddedildi',
'accepted' => 'Kabul Edildi',
'waiting_document' => 'Belge Bekleniyor',
'waiting_document' => 'İmzalı Staj Formu Bekleniyor',
// Wizard and Mermaid
'step_1_title' => '1. Şartlar & Koşullar',
'step_2_title' => '2. Formları İndir',
'step_3_title' => '3. Başvuruyu Tamamla',
'kvkk_title' => 'Belgelerin Toplanma Amacı (KVKK Bilgilendirmesi)',
'kvkk_nda_title' => '1. Gizlilik Sözleşmesi (NDA)',
'kvkk_nda_desc' => 'Projelerimize ait kaynak kodları, veritabanı şemaları ve ticari sırlarımızın güvenliğini sağlamak amacıyla istenir.',
'kvkk_contract_title' => '2. Bağımsız Sözleşmeli Sözleşmesi',
'kvkk_contract_desc' => 'Issue/task bazlı esnek çalışma modelinin koşullarını ve haklarınızı yasal olarak güvence altına almak için gereklidir.',
'kvkk_id_title' => '3. Kimlik Fotokopisi',
'kvkk_id_desc' => 'Yasal kimlik doğrulama, imza yetkilisi tespiti ve sahte başvuruların önlenmesi amacıyla talep edilmektedir.',
'hr_flow_diagram' => 'İK İlerleme Diyagramı',
'terms_accept_label' => 'Çalışma modelini, başvuru şartlarını ve İK süreç akış şemasını okudum, kabul ediyorum.',
'accept_terms_btn' => 'Şartları Kabul Et ve İlerle',
'download_required_documents' => 'Gerekli Belgeleri İndirin',
'download_required_documents_desc' => 'Başvurunuzu tamamlayabilmek için aşağıdaki yasal sözleşmeleri indirip, dikkatlice okuyup imzalayarak bir sonraki adımda sisteme yüklemeniz gerekmektedir.',
'nda_card_desc' => 'Trunçgil Teknoloji projelerine ait kaynak kodlarının, verilerin ve süreçlerin gizliliğini korumak amacıyla imzalanması zorunlu olan Gizlilik Sözleşmesi\'dir.',
'review_download_document' => 'Sözleşmeyi İncele & İndir',
'contract_card_desc' => 'İş/Issue bazlı uzaktan çalışma koşullarını, ödeme şartlarını ve tarafların haklarını tanımlayan Bağımsız Sözleşmeli Hizmet Sözleşmesi\'dir.',
'go_back' => 'Geri Dön',
'downloaded_next_btn' => 'Sözleşmeleri İndirdim, Son Adıma Geç',
'name_placeholder' => 'Örn: Ahmet Yılmaz',
'email_placeholder' => 'Örn: ahmet@example.com',
'phone_placeholder' => 'Örn: 05XX XXX XX XX',
'id_photocopy' => 'Kimlik Fotokopisi',
'id_photocopy_description' => 'Önlü arkalı nüfus cüzdanı, yeni kimlik kartı veya ehliyet fotokopisi (PDF, JPG, PNG - Maks. 5MB)',
// Mermaid Nodes
'mermaid_start' => 'Başvuru ve Gizlilik Sözleşmesi',
'mermaid_dec1' => 'Aday Onaylandı mı?',
'mermaid_red1' => 'Red - Süreç Sonlanır',
'mermaid_git' => 'GitHub Repo Erişimi Sağlanır',
'mermaid_sample' => 'Aday Projeyi Kurar ve Örnek Görev Üzerinde Çalışır',
'mermaid_dec2' => 'Örnek Görev Başarıyla Tamamlandı mı?',
'mermaid_red2' => 'Red - Süreç Sonlanır',
'mermaid_issues' => 'Diğer Issue\'lara Devam',
'mermaid_dec3' => 'Aday Issue Seçti mi?',
'mermaid_wait' => 'Aday Beklemede veya Başka Görev Seçer',
'mermaid_assign' => 'Issue Ataması Yapılır',
'mermaid_work' => 'Çalışma Başlar',
'mermaid_review' => 'Teslim ve İnceleme',
'mermaid_dec4' => 'Görev Onaylandı mı?',
'mermaid_feedback' => 'Düzeltme Talebi ve Geri Bildirim',
'mermaid_payment' => 'Ücret Ödemesi Yapılır',
// Internship Wizard keys
'dear_intern' => 'Değerli Stajyer Adayı,',
'intern_welcome_text_1' => 'Trunçgil Teknoloji olarak, uzaktan staj programımız aracılığıyla genç yeteneklerin gelişimine katkıda bulunmaktan gurur duyuyoruz. Stajyerlerimizi gerçek projelerde değerlendirerek onlara eşsiz bir uzaktan çalışma ve ekip koordinasyonu deneyimi kazandırıyoruz.',
'intern_welcome_text_2' => 'Başvurunuzun değerlendirilebilmesi için öncelikle çalışma modelimizi ve başvuru şartlarımızı incelemeniz, ardından staj başvuru şartlarını kabul ederek formu eksiksiz doldurmanız gerekmektedir.',
'intern_terms_accept_label' => 'Uzaktan staj çalışma modelini, başvuru şartlarını ve İK süreç akışını okudum, kabul ediyorum.',
'accept_intern_terms_btn' => 'Şartları Kabul Et ve Başvuru Formuna Geç',
'intern_step_1_title' => '1. Şartlar & Koşullar',
'intern_step_2_title' => '2. Başvuruyu Tamamla',
'intern_flow_diagram' => 'Stajyer Kabul ve Çalışma Sistemi Diyagramı',
// Intern Mermaid Nodes
'intern_mermaid_start' => 'Staj Başvurusu ve Evrak İletimi',
'intern_mermaid_dec1' => 'Ön Değerlendirme Uygun mu?',
'intern_mermaid_red1' => 'Elendi - Süreç Sonlanır',
'intern_mermaid_doc_upload' => 'Üniversite Staj Formunun Yüklenmesi',
'intern_mermaid_doc_sign' => 'Formun Kurumca İmzalanması & İndirilmesi',
'intern_mermaid_git' => 'Süreç Oryantasyonu Başlar',
'intern_mermaid_sample' => 'GIT ve Teknik Temel Eğitimi',
'intern_mermaid_dec2' => 'GIT Bilgisi Var mı?',
'intern_mermaid_red2' => 'Elendi - Süreç Sonlanır',
'intern_mermaid_real' => 'Gerçek Projeye Dahil Edilme',
'intern_mermaid_dec3' => 'Yeni Görev / Issue Seçimi',
'intern_mermaid_wait' => 'Mentör Desteği ve Geliştirme',
'intern_mermaid_assign' => 'Görev Ataması ve Kodlama',
'intern_mermaid_work' => 'Çalışma ve Günlük Standup',
'intern_mermaid_review' => 'Kod İncelemesi (Code Review)',
'intern_mermaid_dec4' => 'PR Onaylandı mı (Merge)?',
'intern_mermaid_feedback' => 'Düzeltme Talebi (Revizyon)',
'intern_mermaid_success' => 'Başarılı Görev & Deneyim',
'intern_mermaid_graduation' => 'Trunçgil İmzalı Staj Bitirme Sertifikası',
];
+10 -1
View File
@@ -109,7 +109,16 @@ return [
'spotify_no_albums' => 'Spotify sanatçı hesabında albüm veya single bulunamadı.',
'spotify_untitled_album' => 'İsimsiz Albüm',
// YouTube
// Metadata table labels
'field' => 'Alan',
'value' => 'Değer',
'track' => 'Parça',
'style' => 'Stil',
'contributors' => 'Katkıda Bulunanlar',
'released_on' => 'Yayın Tarihi',
'distributor' => 'Dağıtıcı',
'label' => 'Etiket',
'youtube_section' => 'YouTube Bilgileri',
'youtube_video_id_field' => 'YouTube Video ID',
'youtube_url_field' => 'YouTube URL',
+6 -6
View File
@@ -1,38 +1,38 @@
@import url(https://fonts.googleapis.com/css2?family=IBM+Plex+Serif:ital,wght@1,300;1,400;1,500;1,600;1,700);
@import url(https://fonts.googleapis.com/css2?family=IBM+Plex+Serif:ital,wght@1,300;1,400;1,500;1,600;1,700&display=swap);
@font-face {
font-family: 'Space Grotesk';
src: url(../../fonts/space/SpaceGrotesk-SemiBold.woff2) format('woff2'), url(../../fonts/space/SpaceGrotesk-SemiBold.woff) format('woff');
font-weight: 600;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url(../../fonts/space/SpaceGrotesk-Light.woff2) format('woff2'), url(../../fonts/space/SpaceGrotesk-Light.woff) format('woff');
font-weight: 300;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url(../../fonts/space/SpaceGrotesk-Bold.woff2) format('woff2'), url(../../fonts/space/SpaceGrotesk-Bold.woff) format('woff');
font-weight: 700;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url(../../fonts/space/SpaceGrotesk-Medium.woff2) format('woff2'), url(../../fonts/space/SpaceGrotesk-Medium.woff) format('woff');
font-weight: 500;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url(../../fonts/space/SpaceGrotesk-Regular.woff2) format('woff2'), url(../../fonts/space/SpaceGrotesk-Regular.woff) format('woff');
font-weight: 400;
font-style: normal;
font-display: block
font-display: swap;
}
* {
word-spacing: normal !important
+10 -10
View File
@@ -3,70 +3,70 @@
src: url(../../fonts/urbanist/Urbanist-BoldItalic.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-BoldItalic.woff) format('woff');
font-weight: 700;
font-style: italic;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-SemiBoldItalic.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-SemiBoldItalic.woff) format('woff');
font-weight: 600;
font-style: italic;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-Medium.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-Medium.woff) format('woff');
font-weight: 500;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-MediumItalic.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-MediumItalic.woff) format('woff');
font-weight: 500;
font-style: italic;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-SemiBold.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-SemiBold.woff) format('woff');
font-weight: 600;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-Italic.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-Italic.woff) format('woff');
font-weight: 400;
font-style: italic;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-Regular.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-Regular.woff) format('woff');
font-weight: 400;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-LightItalic.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-LightItalic.woff) format('woff');
font-weight: 300;
font-style: italic;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-Light.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-Light.woff) format('woff');
font-weight: 300;
font-style: normal;
font-display: block
font-display: swap;
}
@font-face {
font-family: Urbanist;
src: url(../../fonts/urbanist/Urbanist-Bold.woff2) format('woff2'), url(../../fonts/urbanist/Urbanist-Bold.woff) format('woff');
font-weight: 700;
font-style: normal;
font-display: block
font-display: swap;
}
* {
word-spacing: normal !important
+2 -2
View File
@@ -3,12 +3,12 @@
src:url(Unicons.woff2) format("woff2"),url(Unicons.woff) format("woff");
font-weight:400;
font-style:normal;
font-display:block
font-display:swap;
}
@font-face {
font-family:Custom;
src:url(../custom/Custom.woff2) format("woff2"),url(../custom/Custom.woff) format("woff");
font-weight:400;
font-style:normal;
font-display:block
font-display:swap;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

@@ -0,0 +1 @@
google-site-verification: googlef1M6GQLxVA5g7IaVY6kMQCoIrgADR6HjrOZu79CrXYc.html
+1 -31862
View File
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -1,2 +1,6 @@
User-agent: *
Disallow:
Allow: /
Disallow: /admin/
Disallow: /stajyer/admin/
Sitemap: https://truncgil.com/sitemap.xml
@@ -1,15 +1,6 @@
<div class="navbar-other !flex !ml-auto">
<ul class="navbar-nav !flex-row !items-center !ml-auto">
<li class="nav-item hidden lg:!block xl:!block">
<nav class="nav social social-muted justify-end text-right">
<?php $social_media = json_decode(setting('social_links'), true); ?>
<a class="m-[0_0_0_.7rem] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem]" href="{{ !empty($social_media['Twitter']) ? $social_media['Twitter'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}"><i class="uil uil-twitter before:content-['\ed59'] text-[1rem] !text-[#5daed5]" aria-hidden="true"></i></a>
<a class="m-[0_0_0_.7rem] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem]" href="{{ !empty($social_media['Facebook']) ? $social_media['Facebook'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Facebook') }}"><i class="uil uil-facebook-f before:content-['\eae2'] text-[1rem] !text-[#4470cf]" aria-hidden="true"></i></a>
<a class="m-[0_0_0_.7rem] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem]" href="{{ !empty($social_media['Github']) ? $social_media['Github'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}"><i class="uil uil-github before:content-['\eb40'] text-[1rem] !text-[#e94d88]" aria-hidden="true"></i></a>
<a class="m-[0_0_0_.7rem] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem]" href="{{ !empty($social_media['Instagram']) ? $social_media['Instagram'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}"><i class="uil uil-instagram before:content-['\eb9c'] text-[1rem] !text-[#d53581]" aria-hidden="true"></i></a>
</nav>
<!-- /.social -->
</li>
<li class="nav-item xl:!hidden lg:!hidden">
<button class="hamburger offcanvas-nav-btn" type="button" aria-label="{{ t('Menüyü aç') }}"><span></span></button>
</li>
@@ -69,14 +69,7 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
<div>
<a href="mailto:{{setting('contact_email')}}" class="link-inverse">{{setting('contact_email')}}</a>
<br> <a href="tel:{{setting('contact_phone')}}">{{setting('contact_phone')}}</a> <br>
<nav class="nav social social-white !mt-4">
<?php $social_media = json_decode(setting('social_links'), true); ?>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Twitter']) ? $social_media['Twitter'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}"><i class="uil uil-twitter before:content-['\ed59'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Facebook']) ? $social_media['Facebook'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Facebook') }}"><i class="uil uil-facebook-f before:content-['\eae2'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Github']) ? $social_media['Github'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}"><i class="uil uil-github before:content-['\eb40'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Instagram']) ? $social_media['Instagram'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}"><i class="uil uil-instagram before:content-['\eb9c'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Youtube']) ? $social_media['Youtube'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('YouTube') }}"><i class="uil uil-youtube before:content-['\edb5'] !text-white text-[1rem]" aria-hidden="true"></i></a>
</nav>
</div>
</div>
</div>
@@ -105,34 +98,6 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
<!-- Social Media & Language on Right -->
<div class="flex items-center gap-4">
<nav class="nav social flex items-center gap-2">
<?php $social_media = json_decode(setting('social_links'), true); ?>
@if(!empty($social_media['Twitter']))
<a class="!text-[#5daed5] hover:!text-[#3b9ec9] text-[1.1rem] transition-colors" href="{{ $social_media['Twitter'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}">
<i class="uil uil-twitter" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Facebook']))
<a class="!text-[#4470cf] hover:!text-[#3259b8] text-[1.1rem] transition-colors" href="{{ $social_media['Facebook'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Facebook') }}">
<i class="uil uil-facebook-f" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Github']))
<a class="!text-[#343a40] hover:!text-[#23272b] text-[1.1rem] transition-colors" href="{{ $social_media['Github'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}">
<i class="uil uil-github" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Instagram']))
<a class="!text-[#d53581] hover:!text-[#c12570] text-[1.1rem] transition-colors" href="{{ $social_media['Instagram'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}">
<i class="uil uil-instagram" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Youtube']))
<a class="!text-[#c8312b] hover:!text-[#b02822] text-[1.1rem] transition-colors" href="{{ $social_media['Youtube'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('YouTube') }}">
<i class="uil uil-youtube" aria-hidden="true"></i>
</a>
@endif
</nav>
<x-custom.language-selector />
</div>
</div>
@@ -0,0 +1 @@
{{-- Bu bileşen görünümü devre dışı bırakılmıştır. --}}
@@ -0,0 +1,283 @@
@php
$githubRepo = $get('github_repo') ?? ($getRecord ? $getRecord()->github_repo : null);
@endphp
<div
x-data="{
githubRepoUrl: @js($githubRepo),
commitDays: [],
activeDayIndex: 0,
loading: false,
errorMsg: '',
isEmpty: false,
init() {
this.loadCommits();
},
escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.appendChild(document.createTextNode(text));
return div.innerHTML;
},
loadCommits() {
if (!this.githubRepoUrl) {
this.isEmpty = true;
return;
}
this.loading = true;
this.errorMsg = '';
this.isEmpty = false;
this.commitDays = [];
let repoClean = this.githubRepoUrl.replace(/https?:\/\/(www\.)?github\.com\//i, '');
repoClean = repoClean.replace(/\/$/, '');
const parts = repoClean.split('/');
if (parts.length < 2) {
this.loading = false;
this.errorMsg = 'Github depo URL\'si çözümlenemedi. Geçerli format: https://github.com/kullanici/depo';
return;
}
const owner = parts[0];
const repo = parts[1].replace(/\.git$/i, '');
fetch('https://api.github.com/repos/' + owner + '/' + repo + '/commits?per_page=100')
.then(response => {
if (!response.ok) {
if (response.status === 404) {
throw new Error('Depo bulunamadı veya private (gizli). Deponuzun public (herkese açık) olduğundan emin olun.');
} else if (response.status === 403) {
throw new Error('GitHub API istek limiti aşıldı. Lütfen daha sonra tekrar deneyin.');
} else {
throw new Error('GitHub API hatası: ' + response.statusText);
}
}
return response.json();
})
.then(commits => {
this.loading = false;
if (!Array.isArray(commits) || commits.length === 0) {
this.isEmpty = true;
return;
}
commits.reverse();
const grouped = {};
commits.forEach(item => {
const dateStr = item.commit.author.date;
if (dateStr) {
const dateOnly = dateStr.substring(0, 10);
if (!grouped[dateOnly]) {
grouped[dateOnly] = [];
}
grouped[dateOnly].push(item);
}
});
const sortedDates = Object.keys(grouped).sort();
this.commitDays = sortedDates.map((date, index) => {
const dateParts = date.split('-');
return {
date: date,
dayNum: index + 1,
formattedDate: dateParts[2] + '.' + dateParts[1] + '.' + dateParts[0],
commits: grouped[date].map(c => ({
message: c.commit.message,
time: new Date(c.commit.author.date).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }),
sha: c.sha.substring(0, 7),
url: c.html_url
}))
};
});
if (this.commitDays.length === 0) {
this.isEmpty = true;
return;
}
this.activeDayIndex = 0;
})
.catch(err => {
this.loading = false;
this.errorMsg = err.message || 'Bir hata oluştu.';
});
},
get activeDay() {
return this.commitDays[this.activeDayIndex] || null;
},
navigateDay(dir) {
const newIndex = this.activeDayIndex + dir;
if (newIndex >= 0 && newIndex < this.commitDays.length) {
this.activeDayIndex = newIndex;
}
},
selectDay(index) {
this.activeDayIndex = index;
}
}"
style="margin-top: 1rem; font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;"
>
<style>
.jt-day-btn {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.jt-day-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
}
.jt-arrow-btn {
transition: all 0.2s ease;
}
.jt-arrow-btn:hover:not(:disabled) {
transform: scale(1.08);
background: #eff6ff !important;
border-color: #93c5fd !important;
}
.jt-arrow-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.jt-event-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.jt-event-card:hover {
transform: translateY(-2px) translateX(3px);
box-shadow: 0 12px 24px -8px rgba(59, 130, 246, 0.12);
border-color: #bfdbfe !important;
background: #ffffff !important;
}
.jt-timeline-dot {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.jt-event-row:hover .jt-timeline-dot {
transform: scale(1.2);
background-color: #3b82f6 !important;
box-shadow: 0 0 0 5px rgba(59, 130, 246, 0.18);
}
.jt-event-row:hover .jt-timeline-dot-inner {
background-color: #ffffff !important;
}
.jt-sha-link {
transition: all 0.25s ease;
}
.jt-sha-link:hover {
background: #3b82f6 !important;
color: #ffffff !important;
border-color: #3b82f6 !important;
}
.jt-paginator-scroll::-webkit-scrollbar {
height: 4px;
}
.jt-paginator-scroll::-webkit-scrollbar-track {
background: transparent;
}
.jt-paginator-scroll::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 9999px;
}
.jt-paginator-scroll {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
}
@keyframes jt-spin {
to { transform: rotate(360deg); }
}
@keyframes jt-fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.jt-fade-in {
animation: jt-fadeIn 0.4s ease-out;
}
</style>
{{-- Loader --}}
<div x-show="loading" x-cloak style="padding: 3rem 0; display: flex; flex-direction: column; align-items: center; justify-content: center;">
<div style="width: 2.5rem; height: 2.5rem; border: 2px solid #e2e8f0; border-top-color: #3b82f6; border-radius: 50%; animation: jt-spin 0.8s linear infinite; margin-bottom: 0.75rem;"></div>
<span style="font-size: 0.75rem; color: #94a3b8; font-weight: 600; letter-spacing: 0.025em;">Commit geçmişi yükleniyor...</span>
</div>
{{-- Error --}}
<div x-show="errorMsg" x-cloak x-text="errorMsg" style="padding: 1rem 1.25rem; border-radius: 1rem; background: #fef2f2; border: 1px solid #fecaca; color: #dc2626; font-size: 0.75rem; font-weight: 700;"></div>
{{-- Empty --}}
<div x-show="isEmpty && !loading" x-cloak style="padding: 2rem; border-radius: 1.5rem; border: 1px solid #f1f5f9; background: #ffffff; text-align: center; color: #94a3b8; font-style: italic; font-size: 0.875rem;">
Henüz hiçbir commit verisi bulunamadı veya depo adresi yapılandırılmadı.
</div>
{{-- Day Paginator --}}
<div x-show="commitDays.length > 0" x-cloak style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem; background: rgba(255,255,255,0.8); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid #e2e8f0; border-radius: 1.5rem; margin-bottom: 1.75rem; user-select: none; box-shadow: 0 1px 3px rgba(0,0,0,0.04);">
<button type="button" @click="navigateDay(-1)" :disabled="activeDayIndex === 0" class="jt-arrow-btn" style="width: 2.75rem; height: 2.75rem; border-radius: 0.875rem; background: #ffffff; color: #64748b; border: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
</button>
<div class="jt-paginator-scroll" style="flex: 1; overflow-x: auto; padding: 0.375rem 0;">
<div style="display: flex; gap: 0.625rem; justify-content: center; min-width: max-content; padding: 0 0.5rem;">
<template x-for="(day, index) in commitDays" :key="day.date">
<button type="button" @click="selectDay(index)" class="jt-day-btn" :style="index === activeDayIndex
? 'padding: 0.5rem 1.25rem; border-radius: 0.875rem; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 1.5px solid #93c5fd; background: linear-gradient(135deg, #eff6ff, #dbeafe); color: #1d4ed8; font-weight: 800; box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15);'
: 'padding: 0.5rem 1.25rem; border-radius: 0.875rem; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 1px solid #e2e8f0; background: #ffffff; color: #64748b; font-weight: 600;'">
<span style="font-size: 0.75rem; line-height: 1.2;" x-text="day.dayNum + '. Gün'"></span>
<span style="font-size: 0.6rem; margin-top: 0.125rem; opacity: 0.7; font-weight: 500;" x-text="day.formattedDate"></span>
</button>
</template>
</div>
</div>
<button type="button" @click="navigateDay(1)" :disabled="activeDayIndex === commitDays.length - 1" class="jt-arrow-btn" style="width: 2.75rem; height: 2.75rem; border-radius: 0.875rem; background: #ffffff; color: #64748b; border: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
{{-- Active Day Card --}}
<div x-show="activeDay" x-cloak class="jt-fade-in" :key="activeDayIndex" style="background: #ffffff; border: 1px solid #f1f5f9; border-radius: 1.5rem; padding: 1.75rem 2rem; box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.06);">
{{-- Day Header --}}
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.75rem; padding-bottom: 1.25rem; border-bottom: 1px solid #f1f5f9;">
<div style="display: flex; align-items: center; gap: 0.875rem;">
<span style="font-size: 0.875rem; font-weight: 800; color: #2563eb; background: linear-gradient(135deg, #eff6ff, #dbeafe); padding: 0.5rem 1.125rem; border-radius: 1rem; box-shadow: 0 1px 3px rgba(37, 99, 235, 0.1);" x-text="activeDay ? activeDay.dayNum + '. Gün' : ''"></span>
<span style="font-size: 0.8125rem; font-weight: 600; color: #94a3b8; display: flex; align-items: center; gap: 0.375rem;">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: #60a5fa;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<span x-text="activeDay ? activeDay.formattedDate : ''"></span>
</span>
</div>
<span style="font-size: 0.6875rem; font-weight: 700; color: #94a3b8; background: #f8fafc; border: 1px solid #f1f5f9; padding: 0.375rem 0.875rem; border-radius: 0.625rem;" x-text="activeDay ? activeDay.commits.length + ' Commit' : ''"></span>
</div>
{{-- Timeline --}}
<div style="position: relative; padding-left: 2.25rem;">
{{-- Gradient Timeline Line --}}
<div style="position: absolute; left: 11px; top: 8px; bottom: 8px; width: 2px; background: linear-gradient(180deg, #3b82f6 0%, #818cf8 40%, #c7d2fe 100%); border-radius: 2px;"></div>
<div style="display: flex; flex-direction: column; gap: 1.25rem;">
<template x-for="(commit, ci) in (activeDay ? activeDay.commits : [])" :key="ci">
<div class="jt-event-row" style="position: relative;">
{{-- Timeline Dot --}}
<div class="jt-timeline-dot" style="position: absolute; left: -32px; top: 6px; width: 24px; height: 24px; border-radius: 50%; background: #ffffff; border: 2.5px solid #3b82f6; display: flex; align-items: center; justify-content: center; z-index: 1;">
<div class="jt-timeline-dot-inner" style="width: 8px; height: 8px; border-radius: 50%; background: #3b82f6;"></div>
</div>
{{-- Event Card --}}
<div class="jt-event-card" style="padding: 1.125rem 1.25rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.03);">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.625rem;">
<span style="font-size: 0.75rem; font-weight: 700; color: #94a3b8; display: flex; align-items: center; gap: 0.375rem;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#60a5fa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<span x-text="commit.time"></span>
</span>
<a :href="commit.url" target="_blank" class="jt-sha-link" x-text="commit.sha" style="font-size: 0.6875rem; font-family: 'SF Mono', 'Fira Code', monospace; font-weight: 700; color: #2563eb; background: #eff6ff; padding: 0.25rem 0.75rem; border-radius: 0.5rem; border: 1px solid #bfdbfe; text-decoration: none;"></a>
</div>
<p style="font-size: 0.8125rem; font-weight: 600; color: #334155; line-height: 1.6; white-space: pre-line; margin: 0;" x-text="commit.message"></p>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
@@ -0,0 +1,642 @@
@extends('layouts.site')
@section('content')
<section class="wrapper bg-[#f0f7ff] py-12">
<div class="container px-4">
<div class="max-w-7xl mx-auto">
<!-- Welcome Section -->
@include('front.career.partials.welcome')
<!-- Statistics Section -->
@include('front.career.partials.stats')
<!-- Tab Switcher Menu -->
<div class="bg-white rounded-3xl p-4 shadow-xl border border-slate-100/50 mb-8">
<div class="flex flex-col sm:flex-row gap-3" role="tablist">
<button type="button" role="tab" aria-selected="true" data-tab-target="gantt-chart" class="admin-tab-btn active flex-1 flex items-center justify-center gap-3 py-4 px-6 rounded-2xl text-center font-bold text-sm transition-all border border-transparent cursor-pointer">
<i class="uil uil-schedule text-lg"></i>
<span>Gantt Şeması ve Takvimi</span>
</button>
<button type="button" role="tab" aria-selected="false" data-tab-target="intern-list" class="admin-tab-btn flex-1 flex items-center justify-center gap-3 py-4 px-6 rounded-2xl text-center font-bold text-sm transition-all border border-transparent text-slate-600 hover:bg-slate-50 cursor-pointer">
<i class="uil uil-list-ul text-lg"></i>
<span>Stajyer Listesi ve Detaylı Takip</span>
</button>
</div>
</div>
<!-- Tab Panels -->
<div id="gantt-chart-panel" class="admin-tab-panel space-y-6">
@include('front.career.partials.gantt')
</div>
<div id="intern-list-panel" class="admin-tab-panel hidden space-y-6">
@include('front.career.partials.list')
</div>
</div>
</div>
</section>
@include('front.career.partials.guide_modal')
<!-- Intern Journal Tracking & Approval Modal -->
<div id="intern-journal-modal" class="fixed inset-0 z-[9998] hidden items-center justify-center bg-slate-900/60 backdrop-blur-sm transition-opacity duration-300">
<div class="bg-white rounded-3xl shadow-2xl border border-slate-100 max-w-4xl w-full mx-4 overflow-hidden transform scale-95 opacity-0 transition-all duration-300 flex flex-col max-h-[90vh]">
<!-- Header -->
<div class="px-6 py-4 bg-gradient-to-r from-blue-50 to-indigo-50/30 border-b border-slate-100 flex items-center justify-between flex-shrink-0">
<div>
<h4 id="ijm-name" class="font-bold text-slate-800 text-lg">Stajyer Defteri ve İnceleme</h4>
<p id="ijm-meta" class="text-xs text-slate-500 font-semibold mt-0.5"></p>
</div>
<button type="button" onclick="closeInternJournalModal()" class="text-slate-400 hover:text-slate-600 transition-colors p-1.5 rounded-full hover:bg-slate-100/80">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
<!-- Body -->
<div class="flex-grow p-6 overflow-y-auto no-scrollbar grid grid-cols-1 md:grid-cols-12 gap-6">
<!-- Left Sidebar: Progress & General Approvals (col-span-4) -->
<div class="md:col-span-4 space-y-6">
<!-- Progress Bar Card -->
<div class="bg-slate-50 rounded-2xl p-5 border border-slate-200/50">
<h5 class="font-bold text-slate-700 text-xs uppercase tracking-wider mb-4 flex items-center gap-1.5">
<i class="uil uil-chart-bar text-blue-600"></i>
<span>Staj İlerlemesi</span>
</h5>
<!-- Journal Filling Progress -->
<div class="space-y-2 mb-4">
<div class="flex justify-between text-xs font-bold">
<span class="text-slate-500">Defter Doldurma</span>
<span id="ijm-fill-text" class="text-slate-700">0 / 0 Gün</span>
</div>
<div class="w-full bg-slate-200 h-2.5 rounded-full overflow-hidden">
<div id="ijm-fill-progress" class="bg-blue-600 h-full rounded-full transition-all duration-500" style="width: 0%"></div>
</div>
</div>
<!-- Journal Approval Progress -->
<div class="space-y-2">
<div class="flex justify-between text-xs font-bold">
<span class="text-slate-500">Onaylı Günler</span>
<span id="ijm-approved-text" class="text-slate-700">0 / 0 Gün</span>
</div>
<div class="w-full bg-slate-200 h-2.5 rounded-full overflow-hidden">
<div id="ijm-approved-progress" class="bg-emerald-500 h-full rounded-full transition-all duration-500" style="width: 0%"></div>
</div>
</div>
</div>
<!-- Overall Signatures Card -->
<div class="bg-slate-50 rounded-2xl p-5 border border-slate-200/50 space-y-4">
<h5 class="font-bold text-slate-700 text-xs uppercase tracking-wider flex items-center gap-1.5">
<i class="uil uil-signature text-indigo-600"></i>
<span>Resmi Onaylar / İmzalar</span>
</h5>
<!-- Supervisor Signature -->
<div class="p-3 bg-white rounded-xl border border-slate-100 flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-xs font-bold text-slate-600">Sorumlu İmzası</span>
<button type="button" id="ijm-btn-supervisor" onclick="toggleNotebookSignature('supervisor')" class="px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border transition-all"></button>
</div>
<div class="text-[10px] text-slate-400 font-semibold" id="ijm-text-supervisor">İmzalanmadı</div>
</div>
<!-- Unit Signature -->
<div class="p-3 bg-white rounded-xl border border-slate-100 flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-xs font-bold text-slate-600">Birim Sorumlu İmzası</span>
<button type="button" id="ijm-btn-unit" onclick="toggleNotebookSignature('unit')" class="px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border transition-all"></button>
</div>
<div class="text-[10px] text-slate-400 font-semibold" id="ijm-text-unit">İmzalanmadı</div>
</div>
<!-- Notebook Approved -->
<div class="p-3 bg-white rounded-xl border border-slate-100 flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-xs font-bold text-slate-600">Genel Defter Onayı</span>
<button type="button" id="ijm-btn-approved" onclick="toggleNotebookSignature('approved')" class="px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border transition-all"></button>
</div>
<div class="text-[10px] text-slate-400 font-semibold" id="ijm-text-approved">Onaylanmadı</div>
</div>
</div>
</div>
<!-- Right Section: Carousel & Navigation (col-span-8) -->
<div class="md:col-span-8 flex flex-col space-y-4">
<!-- Carousel Controls -->
<div class="bg-slate-50 p-4 rounded-2xl border border-slate-200/50 flex items-center justify-between gap-3">
<button type="button" onclick="prevSlide()" class="p-2 bg-white hover:bg-slate-100 border border-slate-200 text-slate-600 hover:text-slate-900 rounded-xl transition-all flex items-center justify-center cursor-pointer">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M15 19l-7-7 7-7"/></svg>
</button>
<div class="flex-grow flex items-center justify-center gap-3">
<span id="ijm-day-indicator" class="text-sm font-extrabold text-blue-600 bg-blue-50 px-3 py-1.5 rounded-xl">Gün: 0 / 0</span>
<!-- Quick Day Dropdown -->
<select id="ijm-day-select" onchange="goToSlide(this.value)" class="text-xs font-semibold text-slate-700 bg-white border border-slate-200 rounded-xl px-2.5 py-1.5 focus:outline-none focus:border-blue-400">
<!-- Options loaded dynamically -->
</select>
</div>
<button type="button" onclick="nextSlide()" class="p-2 bg-white hover:bg-slate-100 border border-slate-200 text-slate-600 hover:text-slate-900 rounded-xl transition-all flex items-center justify-center cursor-pointer">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg>
</button>
</div>
<!-- Carousel Slide Container -->
<div class="bg-white rounded-2xl border border-slate-100 p-6 flex-grow flex flex-col shadow-sm min-h-[300px]">
<!-- Day Title & Date -->
<div class="flex items-center justify-between pb-3 border-b border-slate-100 mb-4 flex-shrink-0">
<div>
<span id="ijm-slide-title" class="text-sm font-extrabold text-slate-800">Seçili Gün</span>
<span id="ijm-slide-date" class="text-xs font-semibold text-slate-400 ml-2"></span>
</div>
<div id="ijm-slide-retroactive">
<!-- Retroactive badge -->
</div>
</div>
<!-- Slide Content (Rich text HTML) -->
<div class="flex-grow overflow-y-auto no-scrollbar prose max-w-none text-slate-700 leading-relaxed text-sm" style="max-height: 250px;">
<div id="ijm-slide-content">
<!-- Day content loaded here -->
</div>
</div>
<!-- Slide Actions / Daily approval status -->
<div class="pt-4 border-t border-slate-100 flex items-center justify-between mt-4 flex-shrink-0">
<div id="ijm-slide-status-badge">
<!-- Status badge -->
</div>
<button type="button" id="ijm-slide-action-btn" onclick="toggleActiveDayApproval()" class="px-4 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-xl font-bold text-xs shadow-lg shadow-blue-500/20 transition-all cursor-pointer">
Onayla
</button>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="px-6 py-4 bg-slate-50 border-t border-slate-100 flex items-center justify-end flex-shrink-0">
<button type="button" onclick="closeInternJournalModal()" class="px-5 py-2.5 bg-slate-800 hover:bg-slate-900 text-white font-bold rounded-xl text-xs transition-colors shadow-md cursor-pointer">Kapat</button>
</div>
</div>
</div>
@push('styles')
<style>
.admin-tab-btn {
transition: all 0.2s ease-in-out;
}
.admin-tab-btn:hover:not(.active) {
background-color: #f8fafc;
}
.admin-tab-btn.active {
background-color: #eff6ff;
border-color: #dbeafe;
color: #1e40af !important;
}
</style>
@endpush
@push('scripts')
<script>
let currentInternId = null;
let journalEntries = [];
let activeSlideIndex = 0;
let isUpdatingSignature = false;
let isUpdatingDayApproval = false;
function openInternJournalModal(internId) {
currentInternId = internId;
activeSlideIndex = 0;
const modal = document.getElementById('intern-journal-modal');
if (!modal) return;
modal.classList.remove('hidden');
modal.classList.add('flex');
// Animate open
const card = modal.querySelector('.max-w-4xl');
if (card) {
setTimeout(() => {
card.classList.remove('scale-95', 'opacity-0');
card.classList.add('scale-100', 'opacity-100');
}, 50);
}
loadInternJournalDetails(internId);
}
function closeInternJournalModal() {
const modal = document.getElementById('intern-journal-modal');
if (!modal) return;
const card = modal.querySelector('.max-w-4xl');
if (card) {
card.classList.remove('scale-100', 'opacity-100');
card.classList.add('scale-95', 'opacity-0');
}
setTimeout(() => {
modal.classList.add('hidden');
modal.classList.remove('flex');
}, 200);
}
function loadInternJournalDetails(internId) {
const slideContent = document.getElementById('ijm-slide-content');
if (slideContent) {
slideContent.innerHTML = `
<div class="flex items-center justify-center p-12">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
`;
}
fetch(`/stajyer/admin/journal-details?intern_id=${internId}`)
.then(res => res.json())
.then(data => {
if (!data.success) {
alert(data.message || 'Veriler yüklenemedi.');
closeInternJournalModal();
return;
}
// Set Header details
const nameEl = document.getElementById('ijm-name');
if (nameEl) nameEl.textContent = data.intern.name;
let startF = data.intern.start_date ? formatDateStr(data.intern.start_date) : '-';
let endF = data.intern.end_date ? formatDateStr(data.intern.end_date) : '-';
const metaEl = document.getElementById('ijm-meta');
if (metaEl) metaEl.textContent = `${startF} - ${endF} (${data.intern.total_days} İş Günü)`;
// Setup Progress Bars
const fillPercent = data.intern.total_days > 0 ? (data.intern.filled_days / data.intern.total_days) * 100 : 0;
const fillTextEl = document.getElementById('ijm-fill-text');
if (fillTextEl) fillTextEl.textContent = `${data.intern.filled_days} / ${data.intern.total_days} Gün`;
const fillBarEl = document.getElementById('ijm-fill-progress');
if (fillBarEl) fillBarEl.style.width = `${fillPercent}%`;
// Approved days count
let approvedDaysCount = 0;
data.entries.forEach(e => {
if (e.filled && e.supervisor_approved) {
approvedDaysCount++;
}
});
const approvedPercent = data.intern.total_days > 0 ? (approvedDaysCount / data.intern.total_days) * 100 : 0;
const appTextEl = document.getElementById('ijm-approved-text');
if (appTextEl) appTextEl.textContent = `${approvedDaysCount} / ${data.intern.total_days} Gün`;
const appBarEl = document.getElementById('ijm-approved-progress');
if (appBarEl) appBarEl.style.width = `${approvedPercent}%`;
// Setup Signatures
updateSignatureUI('supervisor', data.intern.notebook_supervisor_signed, data.intern.notebook_supervisor_name);
updateSignatureUI('unit', data.intern.notebook_unit_signed, data.intern.notebook_unit_name);
updateSignatureUI('approved', data.intern.notebook_approved, null);
// Setup carousel slide data
journalEntries = data.entries;
// Setup dropdown
const select = document.getElementById('ijm-day-select');
if (select) {
select.innerHTML = '';
journalEntries.forEach((entry, idx) => {
const opt = document.createElement('option');
opt.value = idx;
opt.textContent = `${entry.day_number}. Gün (${entry.formatted_date})` + (entry.filled ? ' [DOLU]' : ' [BOŞ]');
select.appendChild(opt);
});
}
// Set total indicator text
const indEl = document.getElementById('ijm-day-indicator');
if (indEl) indEl.textContent = `Gün: 1 / ${journalEntries.length}`;
// Render first slide
renderSlide(0);
})
.catch(err => {
console.error(err);
alert('Staj detayları yüklenirken bir hata oluştu.');
closeInternJournalModal();
});
}
function formatDateStr(dateStr) {
if (!dateStr) return '';
const parts = dateStr.split('-');
if (parts.length === 3) {
return `${parts[2]}.${parts[1]}.${parts[0]}`;
}
return dateStr;
}
function updateSignatureUI(type, signed, name) {
const btn = document.getElementById(`ijm-btn-${type}`);
const label = document.getElementById(`ijm-text-${type}`);
if (!btn || !label) return;
if (type === 'supervisor') {
if (signed) {
btn.textContent = "İmzayı Kaldır";
btn.className = "px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border border-rose-200 bg-rose-50 text-rose-600 hover:bg-rose-100 transition-all cursor-pointer";
label.innerHTML = `<span class="text-green-600 font-extrabold"><i class="uil uil-check-circle mr-0.5"></i>İmzaladı: ${name || ''}</span>`;
} else {
btn.textContent = "İmzala";
btn.className = "px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border border-indigo-200 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 transition-all cursor-pointer";
label.textContent = "İmzalanmadı";
}
} else if (type === 'unit') {
if (signed) {
btn.textContent = "İmzayı Kaldır";
btn.className = "px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border border-rose-200 bg-rose-50 text-rose-600 hover:bg-rose-100 transition-all cursor-pointer";
label.innerHTML = `<span class="text-green-600 font-extrabold"><i class="uil uil-check-circle mr-0.5"></i>İmzaladı: ${name || ''}</span>`;
} else {
btn.textContent = "İmzala";
btn.className = "px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border border-indigo-200 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 transition-all cursor-pointer";
label.textContent = "İmzalanmadı";
}
} else if (type === 'approved') {
if (signed) {
btn.textContent = "Onayı Kaldır";
btn.className = "px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border border-rose-200 bg-rose-50 text-rose-600 hover:bg-rose-100 transition-all cursor-pointer";
label.innerHTML = `<span class="text-green-600 font-extrabold"><i class="uil uil-check-circle mr-0.5"></i>Defter Onaylandı</span>`;
} else {
btn.textContent = "Onayla";
btn.className = "px-2.5 py-1 text-[10px] font-extrabold uppercase rounded-lg border border-emerald-200 bg-emerald-50 text-emerald-600 hover:bg-emerald-100 transition-all cursor-pointer";
label.textContent = "Onaylanmadı";
}
}
}
function toggleNotebookSignature(type) {
if (isUpdatingSignature) return;
// Get current state
const btn = document.getElementById(`ijm-btn-${type}`);
if (!btn) return;
const isSignedCurrently = btn.textContent.includes('Kaldır');
const newSignState = !isSignedCurrently;
let promptName = '';
if (newSignState && (type === 'supervisor' || type === 'unit')) {
promptName = prompt("İmzalayan yetkili ismini giriniz veya onaylayınız:", @json(auth()->user()->name));
if (promptName === null) return; // cancelled
if (promptName.trim() === '') {
promptName = @json(auth()->user()->name);
}
}
isUpdatingSignature = true;
btn.style.opacity = '0.5';
fetch('/stajyer/admin/toggle-notebook-signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
intern_id: currentInternId,
type: type,
signed: newSignState ? 1 : 0,
name: promptName
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
const key = type === 'supervisor' ? 'notebook_supervisor_signed' : (type === 'unit' ? 'notebook_unit_signed' : 'notebook_approved');
const nameKey = type === 'supervisor' ? 'notebook_supervisor_name' : (type === 'unit' ? 'notebook_unit_name' : null);
updateSignatureUI(type, data.intern[key], data.intern[nameKey]);
} else {
alert(data.message || 'Hata oluştu.');
}
})
.catch(err => {
console.error(err);
alert('İşlem gerçekleştirilemedi.');
})
.finally(() => {
isUpdatingSignature = false;
btn.style.opacity = '1';
});
}
function renderSlide(index) {
if (index < 0 || index >= journalEntries.length) return;
activeSlideIndex = index;
const entry = journalEntries[index];
// Update indicator & select dropdown
const indEl = document.getElementById('ijm-day-indicator');
if (indEl) indEl.textContent = `Gün: ${index + 1} / ${journalEntries.length}`;
const selEl = document.getElementById('ijm-day-select');
if (selEl) selEl.value = index;
// Title & Date
const titleEl = document.getElementById('ijm-slide-title');
if (titleEl) titleEl.textContent = `${entry.day_number}. Gün Raporu`;
const dateEl = document.getElementById('ijm-slide-date');
if (dateEl) dateEl.textContent = entry.formatted_date;
// Retroactive badge
const retroBadge = document.getElementById('ijm-slide-retroactive');
if (retroBadge) {
retroBadge.innerHTML = '';
if (entry.filled && entry.is_retroactive) {
retroBadge.innerHTML = `
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-rose-50 text-rose-700 text-[10px] font-extrabold uppercase border border-rose-100">
<span class="w-1.5 h-1.5 rounded-full bg-rose-500 animate-pulse"></span>
Geriye Dönük
</span>
`;
}
}
// Content area
const contentArea = document.getElementById('ijm-slide-content');
const badgeDiv = document.getElementById('ijm-slide-status-badge');
const actionBtn = document.getElementById('ijm-slide-action-btn');
if (!contentArea || !badgeDiv || !actionBtn) return;
if (!entry.filled) {
contentArea.innerHTML = `
<div class="flex flex-col items-center justify-center p-8 text-center bg-slate-50 rounded-2xl border border-dashed border-slate-200">
<i class="uil uil-file-slash text-3xl text-slate-400 mb-2"></i>
<p class="text-sm font-medium text-slate-500">Bu gün için henüz staj raporu yazılmamıştır.</p>
</div>
`;
badgeDiv.innerHTML = `<span class="text-xs text-slate-400 font-semibold">Boş Rapor</span>`;
actionBtn.classList.add('hidden');
} else {
actionBtn.classList.remove('hidden');
const entryContent = entry.content || '<p class="text-slate-400 italic">Boş içerik.</p>';
contentArea.innerHTML = `<div class="rich-text-content prose max-w-none text-slate-700 leading-relaxed text-sm select-text">${entryContent}</div>`;
updateDayApprovalUI(entry.supervisor_approved, entry.supervisor_name);
}
}
function updateDayApprovalUI(approved, name) {
const badgeDiv = document.getElementById('ijm-slide-status-badge');
const actionBtn = document.getElementById('ijm-slide-action-btn');
if (!badgeDiv || !actionBtn) return;
if (approved) {
badgeDiv.innerHTML = `
<span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-emerald-50 text-emerald-700 text-xs font-bold border border-emerald-100">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
Sorumlu Onayladı ${name ? `(${name})` : ''}
</span>
`;
actionBtn.textContent = "Onayı Kaldır";
actionBtn.className = "px-4 py-2 text-white bg-rose-600 hover:bg-rose-700 rounded-xl font-bold text-xs shadow-lg shadow-rose-500/20 transition-all cursor-pointer";
} else {
badgeDiv.innerHTML = `
<span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-slate-100 text-slate-600 text-xs font-bold border border-slate-200">
Onay Bekliyor
</span>
`;
actionBtn.textContent = "Günü Onayla";
actionBtn.className = "px-4 py-2 text-white bg-emerald-600 hover:bg-emerald-700 rounded-xl font-bold text-xs shadow-lg shadow-emerald-500/20 transition-all cursor-pointer";
}
}
function toggleActiveDayApproval() {
if (isUpdatingDayApproval) return;
const entry = journalEntries[activeSlideIndex];
if (!entry || !entry.entry_id) return;
const actionBtn = document.getElementById('ijm-slide-action-btn');
if (!actionBtn) return;
isUpdatingDayApproval = true;
actionBtn.style.opacity = '0.5';
fetch('/stajyer/admin/toggle-approval', {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": "{{ csrf_token() }}"
},
body: JSON.stringify({
entry_id: entry.entry_id
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
// Update local object
entry.supervisor_approved = data.status;
entry.supervisor_name = data.supervisor_name;
// Update UI
updateDayApprovalUI(data.status, data.supervisor_name);
// Recalculate and update approved progress bar
let approvedDaysCount = 0;
journalEntries.forEach(e => {
if (e.filled && e.supervisor_approved) {
approvedDaysCount++;
}
});
const approvedPercent = journalEntries.length > 0 ? (approvedDaysCount / journalEntries.length) * 100 : 0;
const appTextEl = document.getElementById('ijm-approved-text');
if (appTextEl) appTextEl.textContent = `${approvedDaysCount} / ${journalEntries.length} Gün`;
const appBarEl = document.getElementById('ijm-approved-progress');
if (appBarEl) appBarEl.style.width = `${approvedPercent}%`;
} else {
alert(data.message || "Onay güncellenemedi.");
}
})
.catch(err => {
console.error(err);
alert("İşlem sırasında bir hata oluştu.");
})
.finally(() => {
isUpdatingDayApproval = false;
actionBtn.style.opacity = '1';
});
}
function prevSlide() {
if (activeSlideIndex > 0) {
renderSlide(activeSlideIndex - 1);
}
}
function nextSlide() {
if (activeSlideIndex < journalEntries.length - 1) {
renderSlide(activeSlideIndex + 1);
}
}
function goToSlide(idx) {
renderSlide(parseInt(idx));
}
// Admin Tab Switch Logic
document.addEventListener('DOMContentLoaded', function() {
const adminTabButtons = document.querySelectorAll('.admin-tab-btn');
const adminTabPanels = document.querySelectorAll('.admin-tab-panel');
adminTabButtons.forEach(button => {
button.addEventListener('click', () => {
// Deactivate all buttons
adminTabButtons.forEach(btn => {
btn.classList.remove('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
btn.classList.add('text-slate-600', 'bg-transparent', 'border-transparent');
btn.setAttribute('aria-selected', 'false');
});
// Hide all panels
adminTabPanels.forEach(panel => {
panel.classList.add('hidden');
});
// Activate clicked button
button.classList.add('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
button.classList.remove('text-slate-600', 'bg-transparent', 'border-transparent');
button.setAttribute('aria-selected', 'true');
// Show target panel
const targetId = button.getAttribute('data-tab-target');
const targetPanel = document.getElementById(targetId + '-panel');
if (targetPanel) {
targetPanel.classList.remove('hidden');
// Re-render DevExtreme Gantt chart if visible, to prevent rendering scale bugs
if (targetId === 'gantt-chart') {
const ganttEl = document.getElementById('gantt');
if (ganttEl) {
const ganttInstance = $(ganttEl).dxGantt('instance');
if (ganttInstance) {
ganttInstance.repaint();
}
}
}
}
});
});
});
</script>
@endpush
@endsection
@@ -0,0 +1,87 @@
@extends('layouts.site')
@section('content')
<section class="wrapper bg-[#f0f7ff] min-h-[70vh] flex items-center py-12">
<div class="container px-4">
<div class="max-w-md mx-auto bg-white/80 backdrop-blur-md rounded-3xl shadow-2xl border border-slate-100/50 overflow-hidden">
<div class="p-8 md:p-10">
<div class="text-center mb-8">
<div class="inline-flex w-16 h-16 rounded-full bg-red-50 text-[#e31e24] items-center justify-center mb-4">
<i class="uil uil-shield text-3xl"></i>
</div>
<h1 class="text-3xl font-extrabold text-slate-900 tracking-tight">Yönetici Girişi</h1>
<p class="text-sm text-slate-500 mt-2">Staj takip sistemini yönetmek için e-posta ve şifrenizle giriş yapın.</p>
</div>
@if($errors->any())
<div class="bg-red-50 border-l-4 border-[#e31e24] p-4 rounded-xl mb-6">
<div class="flex">
<div class="flex-shrink-0">
<i class="uil uil-exclamation-triangle text-[#e31e24] text-xl"></i>
</div>
<div class="ml-3">
<p class="text-sm text-red-700 font-medium">
{{ $errors->first() }}
</p>
</div>
</div>
</div>
@endif
@if(session('success'))
<div class="bg-green-50 border-l-4 border-green-500 p-4 rounded-xl mb-6">
<div class="flex">
<div class="flex-shrink-0">
<i class="uil uil-check-circle text-green-500 text-xl"></i>
</div>
<div class="ml-3">
<p class="text-sm text-green-700 font-medium">
{{ session('success') }}
</p>
</div>
</div>
</div>
@endif
<form action="{{ route('intern.admin.login.submit') }}" method="POST" class="space-y-6">
@csrf
<div class="space-y-2">
<label for="email" class="text-sm font-bold text-slate-700 block">E-posta</label>
<div class="relative">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 text-slate-400">
<i class="uil uil-envelope"></i>
</span>
<input type="email" name="email" id="email" class="w-full pl-11 pr-5 py-3.5 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="E-posta adresinizi girin" value="{{ old('email') }}" required>
</div>
</div>
<div class="space-y-2">
<label for="password" class="text-sm font-bold text-slate-700 block">Şifre</label>
<div class="relative">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 text-slate-400">
<i class="uil uil-key-skeleton-alt"></i>
</span>
<input type="password" name="password" id="password" class="w-full pl-11 pr-5 py-3.5 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Şifrenizi girin" required>
</div>
</div>
<button type="submit" class="w-full py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-xl transition-all shadow-lg shadow-red-500/20 flex items-center justify-center gap-2 mt-4 hover:-translate-y-0.5">
<span>Giriş Yap</span>
<i class="uil uil-arrow-right text-xl"></i>
</button>
</form>
</div>
</div>
</div>
</section>
@push('styles')
<script src="https://cdn.tailwindcss.com"></script>
<style>
.shadow-2xl {
box-shadow: 0 1.5rem 4rem rgba(30, 41, 59, 0.08) !important;
}
</style>
@endpush
@endsection
+539 -141
View File
@@ -12,166 +12,358 @@
<section class="wrapper bg-white py-12 lg:py-20">
<div class="container px-4">
<div class="max-w-5xl mx-auto mb-16">
<div class="bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-2xl shadow-sm">
<!-- Wizard Progress Tracker -->
<div class="max-w-3xl mx-auto mb-16">
<div class="relative flex items-center justify-between">
<!-- Progress Line -->
<div class="absolute left-0 top-1/2 -translate-y-1/2 w-full h-1 bg-slate-100 -z-10 rounded-full">
<div id="wizard-progress-bar" class="h-full bg-[#e31e24] rounded-full transition-all duration-500 w-[0%]"></div>
</div>
<!-- Step 1 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-1" class="w-12 h-12 rounded-full bg-[#e31e24] text-white flex items-center justify-center font-extrabold shadow-lg shadow-red-500/20 transition-all duration-500 ring-4 ring-red-100">
<i class="uil uil-info-circle text-xl"></i>
</div>
<span id="step-label-1" class="text-xs md:text-sm font-bold text-slate-800 mt-3 transition-all duration-300">{{ __('career.step_1_title') }}</span>
</div>
<!-- Step 2 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-2" class="w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold transition-all duration-500 ring-4 ring-transparent">
<i class="uil uil-file-download text-xl"></i>
</div>
<span id="step-label-2" class="text-xs md:text-sm font-medium text-slate-400 mt-3 transition-all duration-300">{{ __('career.step_2_title') }}</span>
</div>
<!-- Step 3 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-3" class="w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold transition-all duration-500 ring-4 ring-transparent">
<i class="uil uil-check-circle text-xl"></i>
</div>
<span id="step-label-3" class="text-xs md:text-sm font-medium text-slate-400 mt-3 transition-all duration-300">{{ __('career.step_3_title') }}</span>
</div>
</div>
</div>
<!-- Main Wizard Form Container -->
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" id="career-application-form">
@csrf
<input type="hidden" name="type" value="job">
<!-- ================= STEP 1: TERMS & CONDITIONS ================= -->
<div id="step-content-1" class="wizard-step transition-all duration-500 opacity-100 block">
<div class="max-w-5xl mx-auto mb-12">
<div class="bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-2xl shadow-sm mb-12">
<h4 class="text-xl font-bold text-blue-900 mb-4">{{ __('career.dear_developer') }}</h4>
<div class="text-blue-800 space-y-4 leading-relaxed">
<p>{!! __('career.intro_text_1') !!}</p>
<p>{!! __('career.intro_text_2') !!}</p>
<p>{{ __('career.intro_text_3') }}</p>
<p>{{ __('career.intro_text_4') }}</p>
<p>{{ __('career.thanks') }}</p>
<div class="pt-4 border-t border-blue-200 mt-4 text-sm font-medium">
{!! __('career.company_details') !!}
<p>{!! __('career.intro_text_1') !!}</p>
<p>{!! __('career.intro_text_2') !!}</p>
<div class="bg-white/80 p-6 rounded-2xl border border-blue-100 my-6 space-y-4 text-sm shadow-sm">
<span class="font-extrabold text-blue-950 block text-base"><i class="uil uil-shield-check text-lg mr-1 text-[#e31e24]"></i> {{ __('career.kvkk_title') }}</span>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="p-4 bg-slate-50/50 rounded-xl border border-slate-100">
<span class="font-bold text-slate-800 block mb-2"><i class="uil uil-lock text-[#e31e24] mr-1"></i> {{ __('career.kvkk_nda_title') }}</span>
<p class="text-xs text-slate-600 leading-relaxed">{{ __('career.kvkk_nda_desc') }}</p>
</div>
<div class="p-4 bg-slate-50/50 rounded-xl border border-slate-100">
<span class="font-bold text-slate-800 block mb-2"><i class="uil uil-file-contract-sign text-blue-500 mr-1"></i> {{ __('career.kvkk_contract_title') }}</span>
<p class="text-xs text-slate-600 leading-relaxed">{{ __('career.kvkk_contract_desc') }}</p>
</div>
<div class="p-4 bg-slate-50/50 rounded-xl border border-slate-100">
<span class="font-bold text-slate-800 block mb-2"><i class="uil uil-card-atm text-emerald-500 mr-1"></i> {{ __('career.kvkk_id_title') }}</span>
<p class="text-xs text-slate-600 leading-relaxed">{{ __('career.kvkk_id_desc') }}</p>
</div>
</div>
</div>
</div>
</div>
<div class="text-center mb-12">
<h2 class="text-xs uppercase font-bold tracking-widest text-[#e31e24] mb-2">{{ __('career.working_model_title') }}</h2>
<h3 class="text-3xl md:text-4xl font-extrabold text-slate-900">{{ __('career.working_model_subtitle') }}</h3>
</div>
<div class="flex flex-col lg:flex-row items-center gap-12 mb-20 max-w-6xl mx-auto">
<div class="w-full lg:w-3/5">
<div class="bg-white p-4 md:p-8 rounded-3xl shadow-2xl border border-slate-100 overflow-hidden flex justify-center">
<img src="{{ asset('assets/ik-flowchat.png') }}" alt="{{ __('career.working_model_title') }}" class="w-full h-auto rounded-xl">
</div>
</div>
<div class="w-full lg:w-2/5 space-y-8">
<h4 class="text-2xl font-bold text-slate-900">{{ __('career.terms_title') }}</h4>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.fully_remote') }}</span>
<p class="text-sm text-slate-500">{{ __('career.fully_remote_desc') }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.independent_contractor') }}</span>
<p class="text-sm text-slate-500">{{ __('career.independent_contractor_desc') }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.mandatory_documents') }}</span>
<p class="text-sm text-slate-500">{{ __('career.mandatory_documents_desc') }}</p>
<div class="mt-2 flex flex-wrap gap-3">
<a href="https://docs.google.com/document/d/1WwDo_zgx4KjdnX6ohJaCfPQSS0-eRN41OB1HYvAmDO4/edit?usp=sharing" target="_blank" class="text-xs font-bold text-blue-600 hover:underline flex items-center gap-1">
<i class="uil uil-file-download"></i> {{ __('career.nda_label') }}
</a>
<a href="https://docs.google.com/document/d/1VofpJIxmQmxSLk6OxsDjCtrMte0UkAFCSaQfZCS2DP8/edit?usp=sharing" target="_blank" class="text-xs font-bold text-blue-600 hover:underline flex items-center gap-1">
<i class="uil uil-file-download"></i> {{ __('career.contract_label') }}
</a>
</div>
<p>{{ __('career.intro_text_3') }}</p>
<p>{{ __('career.intro_text_4') }}</p>
<p>{{ __('career.thanks') }}</p>
<div class="pt-4 border-t border-blue-200 mt-4 text-sm font-medium">
{!! __('career.company_details') !!}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-[2rem] shadow-2xl overflow-hidden border border-slate-100">
<div class="p-8 md:p-12">
<div class="mb-10 text-center">
<h2 class="text-3xl font-bold text-slate-900 mb-2">{{ __('career.form_title') }}</h2>
<p class="text-slate-500">{{ __('career.form_subtitle') }}</p>
<div class="w-12 h-1 bg-[#e31e24] rounded-full mx-auto mt-4"></div>
<div class="text-center mb-12">
<h2 class="text-xs uppercase font-bold tracking-widest text-[#e31e24] mb-2">{{ __('career.working_model_title') }}</h2>
<h3 class="text-3xl md:text-4xl font-extrabold text-slate-900">{{ __('career.working_model_subtitle') }}</h3>
</div>
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" class="space-y-6" id="career-application-form">
@csrf
<input type="hidden" name="type" value="job">
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="name" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.name') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="text" name="name" id="name" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Örn: Ahmet Yılmaz" value="{{ old('name') }}" required>
</div>
</div>
<div class="flex flex-col lg:flex-row gap-12 items-start mb-12">
<!-- Mermaid Flowchart Canvas -->
<div class="w-full lg:w-3/5">
<div id="mermaid-card" class="relative bg-white p-6 md:p-8 rounded-3xl shadow-xl border border-slate-100 overflow-x-auto flex flex-col items-center">
<!-- Fullscreen Toggle Button -->
<button type="button" onclick="toggleFullscreen()" class="absolute top-4 right-4 p-2.5 rounded-xl bg-slate-50 hover:bg-slate-100 border border-slate-100 hover:border-slate-200 text-slate-500 hover:text-slate-700 transition-all duration-200 shadow-sm flex items-center justify-center z-10" title="Tam Ekran / Fullscreen">
<i id="fullscreen-icon" class="uil uil-expand-arrows-alt text-lg"></i>
</button>
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest mb-4"><i class="uil uil-presentation-play mr-1"></i> {{ __('career.hr_flow_diagram') }}</span>
<div class="mermaid w-full flex justify-center" style="min-height: 500px;">
graph TD
%% Styles
classDef startNode fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#fff,font-weight:bold,rx:10px,ry:10px;
classDef decisionNode fill:#eab308,stroke:#ca8a04,stroke-width:2px,color:#1e293b,font-weight:bold;
classDef processNode fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
classDef rejectNode fill:#ef4444,stroke:#b91c1c,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
classDef actionNode fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="email" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.email') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="email" name="email" id="email" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Örn: ahmet@example.com" value="{{ old('email') }}" required>
</div>
</div>
Start["{{ __('career.mermaid_start') }}"]:::startNode
Dec1{"{{ __('career.mermaid_dec1') }}"}:::decisionNode
Red1["{{ __('career.mermaid_red1') }}"]:::rejectNode
GitAccess["{{ __('career.mermaid_git') }}"]:::processNode
SampleTask["{{ __('career.mermaid_sample') }}"]:::processNode
Dec2{"{{ __('career.mermaid_dec2') }}"}:::decisionNode
Red2["{{ __('career.mermaid_red2') }}"]:::rejectNode
Issues["{{ __('career.mermaid_issues') }}"]:::processNode
Dec3{"{{ __('career.mermaid_dec3') }}"}:::decisionNode
WaitTask["{{ __('career.mermaid_wait') }}"]:::actionNode
Assign["{{ __('career.mermaid_assign') }}"]:::processNode
WorkStart["{{ __('career.mermaid_work') }}"]:::processNode
Review["{{ __('career.mermaid_review') }}"]:::processNode
Dec4{"{{ __('career.mermaid_dec4') }}"}:::decisionNode
Feedback["{{ __('career.mermaid_feedback') }}"]:::actionNode
Payment["{{ __('career.mermaid_payment') }}"]:::processNode
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="phone" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.phone') }}</label>
<div class="md:col-span-9">
<input type="text" name="phone" id="phone" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Örn: 05XX XXX XX XX" value="{{ old('phone') }}">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="cv" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.cv') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="cv" id="cv" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.cv_description') }}</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="nda" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.nda') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="nda" id="nda" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.nda_description') }}</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="contract" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.contract') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="contract" id="contract" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.contract_description') }}</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-start-4 md:col-span-9 space-y-4">
<div class="flex items-center gap-3">
<input type="checkbox" name="git_knowledge" id="git_knowledge" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24]" required>
<label for="git_knowledge" class="text-sm text-slate-700 font-medium">{{ __('career.git_confirmation') }} <span class="text-[#e31e24]">*</span></label>
</div>
<div class="flex items-center gap-3">
<input type="checkbox" name="ai_usage" id="ai_usage" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24]" required>
<label for="ai_usage" class="text-sm text-slate-700 font-medium">{{ __('career.ai_confirmation') }} <span class="text-[#e31e24]">*</span></label>
Start --> Dec1
Dec1 -- "Hayır" --> Red1
Dec1 -- "Evet" --> GitAccess
GitAccess --> SampleTask
SampleTask --> Dec2
Dec2 -- "Hayır" --> Red2
Dec2 -- "Evet" --> Issues
Issues --> Dec3
Dec3 -- "Hayır" --> WaitTask
Dec3 -- "Evet" --> Assign
Assign --> WorkStart
WorkStart --> Review
Review --> Dec4
Dec4 -- "Hayır" --> Feedback
Feedback --> WorkStart
Dec4 -- "Evet" --> Payment
Payment --> Issues
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-start">
<label for="message" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right pt-3">{{ __('career.message') }}</label>
<div class="md:col-span-9">
<textarea name="message" id="message" class="w-full px-5 py-4 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300 min-h-[150px]" placeholder="{{ __('career.message_placeholder') }}">{{ old('message') }}</textarea>
<!-- Terms & Conditions Bullet Points -->
<div class="w-full lg:w-2/5 space-y-8">
<h4 class="text-2xl font-bold text-slate-900">{{ __('career.terms_title') }}</h4>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.fully_remote') }}</span>
<p class="text-sm text-slate-500">{{ __('career.fully_remote_desc') }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.independent_contractor') }}</span>
<p class="text-sm text-slate-500">{{ __('career.independent_contractor_desc') }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.mandatory_documents') }}</span>
<p class="text-sm text-slate-500">{{ __('career.mandatory_documents_desc') }}</p>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-start-4 md:col-span-9">
<button type="submit" id="submit-btn" class="w-full md:w-auto px-10 py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-full transition-all shadow-lg shadow-red-500/30 flex items-center justify-center gap-3">
<span class="btn-text">{{ __('career.submit') }}</span>
<span class="spinner-border spinner-border-sm hidden animate-spin w-4 h-4 border-2 border-white border-t-transparent rounded-full" role="status"></span>
<i class="uil uil-arrow-right text-xl btn-icon"></i>
<!-- Terms Acceptance Checkbox -->
<div class="bg-slate-50 p-6 rounded-2xl border border-slate-100 mt-8 space-y-4">
<div class="flex items-start gap-3">
<input type="checkbox" id="terms-accept-checkbox" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24] mt-1 cursor-pointer">
<label for="terms-accept-checkbox" class="text-sm text-slate-700 font-semibold cursor-pointer">
{{ __('career.terms_accept_label') }} <span class="text-[#e31e24]">*</span>
</label>
</div>
<button type="button" onclick="nextStep(2)" id="step-1-next-btn" class="w-full py-4 bg-slate-300 text-slate-500 font-bold rounded-xl transition-all flex items-center justify-center gap-2 cursor-not-allowed" disabled>
<span>{{ __('career.accept_terms_btn') }}</span>
<i class="uil uil-arrow-right text-lg"></i>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- ================= STEP 2: DOWNLOAD FORMS ================= -->
<div id="step-content-2" class="wizard-step transition-all duration-500 opacity-0 hidden">
<div class="max-w-4xl mx-auto mb-12">
<div class="text-center mb-10">
<h2 class="text-3xl font-extrabold text-slate-900 mb-2">{{ __('career.download_required_documents') }}</h2>
<p class="text-slate-500 max-w-xl mx-auto">{{ __('career.download_required_documents_desc') }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<!-- NDA Download Card -->
<div class="bg-white p-8 rounded-3xl border border-slate-100 shadow-xl flex flex-col justify-between hover:shadow-2xl hover:border-red-500/20 transition-all duration-300">
<div>
<div class="w-14 h-14 rounded-2xl bg-red-50 flex items-center justify-center text-[#e31e24] mb-6">
<i class="uil uil-shield-check text-3xl"></i>
</div>
<h4 class="text-xl font-bold text-slate-900 mb-3">{{ __('career.nda_label') }}</h4>
<p class="text-sm text-slate-500 leading-relaxed mb-6">{{ __('career.nda_card_desc') }}</p>
</div>
<a href="https://docs.google.com/document/d/1WwDo_zgx4KjdnX6ohJaCfPQSS0-eRN41OB1HYvAmDO4/edit?usp=sharing" target="_blank" class="w-full py-3.5 bg-slate-50 hover:bg-red-50 hover:text-[#e31e24] text-slate-700 font-bold rounded-xl transition-all border border-slate-100 hover:border-red-100 flex items-center justify-center gap-2">
<i class="uil uil-file-download"></i> {{ __('career.review_download_document') }}
</a>
</div>
<!-- Contractor Agreement Download Card -->
<div class="bg-white p-8 rounded-3xl border border-slate-100 shadow-xl flex flex-col justify-between hover:shadow-2xl hover:border-red-500/20 transition-all duration-300">
<div>
<div class="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500 mb-6">
<i class="uil uil-file-contract-sign text-3xl"></i>
</div>
<h4 class="text-xl font-bold text-slate-900 mb-3">{{ __('career.contract_label') }}</h4>
<p class="text-sm text-slate-500 leading-relaxed mb-6">{{ __('career.contract_card_desc') }}</p>
</div>
<a href="https://docs.google.com/document/d/1VofpJIxmQmxSLk6OxsDjCtrMte0UkAFCSaQfZCS2DP8/edit?usp=sharing" target="_blank" class="w-full py-3.5 bg-slate-50 hover:bg-red-50 hover:text-[#e31e24] text-slate-700 font-bold rounded-xl transition-all border border-slate-100 hover:border-red-100 flex items-center justify-center gap-2">
<i class="uil uil-file-download"></i> {{ __('career.review_download_document') }}
</a>
</div>
</div>
<!-- Step 2 Navigation -->
<div class="flex items-center justify-between border-t border-slate-100 pt-8">
<button type="button" onclick="prevStep(1)" class="px-6 py-3.5 bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold rounded-xl transition-all flex items-center gap-2">
<i class="uil uil-arrow-left text-lg"></i>
<span>{{ __('career.go_back') }}</span>
</button>
<button type="button" onclick="nextStep(3)" class="px-8 py-3.5 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-xl transition-all shadow-lg shadow-red-500/20 flex items-center gap-2">
<span>{{ __('career.downloaded_next_btn') }}</span>
<i class="uil uil-arrow-right text-lg"></i>
</button>
</div>
</div>
</div>
<!-- ================= STEP 3: COMPLETE APPLICATION ================= -->
<div id="step-content-3" class="wizard-step transition-all duration-500 opacity-0 hidden">
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-[2rem] shadow-2xl overflow-hidden border border-slate-100">
<div class="p-8 md:p-12">
<div class="mb-10 text-center">
<h2 class="text-3xl font-bold text-slate-900 mb-2">{{ __('career.form_title') }}</h2>
<p class="text-slate-500">{{ __('career.form_subtitle') }}</p>
<div class="w-12 h-1 bg-[#e31e24] rounded-full mx-auto mt-4"></div>
</div>
<div class="space-y-6">
<!-- Name -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="name" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.name') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="text" name="name" id="name" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="{{ __('career.name_placeholder') }}" value="{{ old('name') }}" required>
</div>
</div>
<!-- Email -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="email" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.email') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="email" name="email" id="email" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="{{ __('career.email_placeholder') }}" value="{{ old('email') }}" required>
</div>
</div>
<!-- Phone -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="phone" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.phone') }}</label>
<div class="md:col-span-9">
<input type="text" name="phone" id="phone" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="{{ __('career.phone_placeholder') }}" value="{{ old('phone') }}">
</div>
</div>
<!-- CV Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="cv" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.cv') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="cv" id="cv" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.cv_description') }}</p>
</div>
</div>
<!-- NDA Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="nda" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.nda') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="nda" id="nda" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.nda_description') }}</p>
</div>
</div>
<!-- Contract Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="contract" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.contract') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="contract" id="contract" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.contract_description') }}</p>
</div>
</div>
<!-- ID Photocopy Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="id_photocopy" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.id_photocopy') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="id_photocopy" id="id_photocopy" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.id_photocopy_description') }}</p>
</div>
</div>
<!-- Checkboxes -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-start-4 md:col-span-9 space-y-4">
<div class="flex items-center gap-3">
<input type="checkbox" name="git_knowledge" id="git_knowledge" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24]" required>
<label for="git_knowledge" class="text-sm text-slate-700 font-medium">{{ __('career.git_confirmation') }} <span class="text-[#e31e24]">*</span></label>
</div>
<div class="flex items-center gap-3">
<input type="checkbox" name="ai_usage" id="ai_usage" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24]" required>
<label for="ai_usage" class="text-sm text-slate-700 font-medium">{{ __('career.ai_confirmation') }} <span class="text-[#e31e24]">*</span></label>
</div>
</div>
</div>
<!-- Message -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-start">
<label for="message" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right pt-3">{{ __('career.message') }}</label>
<div class="md:col-span-9">
<textarea name="message" id="message" class="w-full px-5 py-4 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300 min-h-[150px]" placeholder="{{ __('career.message_placeholder') }}">{{ old('message') }}</textarea>
</div>
</div>
<!-- Step 3 Navigation -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 pt-6 border-t border-slate-100">
<div class="md:col-start-4 md:col-span-9 flex items-center justify-between gap-4">
<button type="button" onclick="prevStep(2)" class="px-6 py-4 bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold rounded-xl transition-all flex items-center gap-2">
<i class="uil uil-arrow-left text-lg"></i>
<span>{{ __('career.go_back') }}</span>
</button>
<button type="submit" id="submit-btn" class="flex-1 md:flex-none px-10 py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-full transition-all shadow-lg shadow-red-500/30 flex items-center justify-center gap-3">
<span class="btn-text">{{ __('career.submit') }}</span>
<span class="spinner-border spinner-border-sm hidden animate-spin w-4 h-4 border-2 border-white border-t-transparent rounded-full" role="status"></span>
<i class="uil uil-arrow-right text-xl btn-icon"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</section>
@@ -281,10 +473,212 @@
padding-left: 2rem;
position: relative;
}
/* Mermaid diagram readability and aesthetics */
.mermaid .nodeLabel,
.mermaid .node text,
.mermaid text,
.mermaid .node .label {
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 0 1px rgba(0, 0, 0, 0.2) !important;
padding: 2px 8px !important;
}
.mermaid .node.decisionNode .nodeLabel,
.mermaid .node.decisionNode text,
.mermaid .node.decisionNode .label {
text-shadow: none !important;
color: #1e293b !important;
}
.mermaid svg {
filter: drop-shadow(0px 10px 20px rgba(15, 23, 42, 0.05));
}
/* Fullscreen styles for card container */
#mermaid-card:fullscreen,
#mermaid-card:-webkit-full-screen,
#mermaid-card:-ms-fullscreen {
background-color: #ffffff !important;
padding: 3rem 2rem !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
overflow: auto !important;
border-radius: 0px !important;
width: 100% !important;
height: 100% !important;
}
</style>
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.2.4/dist/mermaid.min.js"></script>
<script>
// Initialize Mermaid
mermaid.initialize({
startOnLoad: false,
theme: 'neutral',
securityLevel: 'loose',
flowchart: {
useMaxWidth: true,
htmlLabels: true
}
});
// Run mermaid after all fonts are fully loaded to ensure boxes match the text dimensions perfectly
if (document.fonts) {
document.fonts.ready.then(function() {
mermaid.run({
nodes: document.querySelectorAll('.mermaid')
});
});
} else {
window.addEventListener('load', function() {
mermaid.run({
nodes: document.querySelectorAll('.mermaid')
});
});
}
// Fullscreen toggle logic for Mermaid card
function toggleFullscreen() {
const card = document.getElementById('mermaid-card');
const icon = document.getElementById('fullscreen-icon');
if (!document.fullscreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
if (card.requestFullscreen) {
card.requestFullscreen();
} else if (card.webkitRequestFullscreen) { /* Safari */
card.webkitRequestFullscreen();
} else if (card.msRequestFullscreen) { /* IE11 */
card.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
}
// Handle icon sync on fullscreen change
function handleFullscreenChange() {
const icon = document.getElementById('fullscreen-icon');
if (document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
icon.className = "uil uil-shrink text-lg";
} else {
icon.className = "uil uil-expand-arrows-alt text-lg";
}
}
document.addEventListener('fullscreenchange', handleFullscreenChange);
document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
document.addEventListener('msfullscreenchange', handleFullscreenChange);
// Step variables
let currentStep = 1;
// Track Terms checkbox to enable Step 1 Next button
document.getElementById('terms-accept-checkbox').addEventListener('change', function(e) {
const nextBtn = document.getElementById('step-1-next-btn');
if (e.target.checked) {
nextBtn.disabled = false;
nextBtn.classList.remove('bg-slate-300', 'text-slate-500', 'cursor-not-allowed');
nextBtn.classList.add('bg-[#e31e24]', 'text-white', 'hover:bg-[#c4191f]', 'shadow-lg', 'shadow-red-500/20');
} else {
nextBtn.disabled = true;
nextBtn.classList.add('bg-slate-300', 'text-slate-500', 'cursor-not-allowed');
nextBtn.classList.remove('bg-[#e31e24]', 'text-white', 'hover:bg-[#c4191f]', 'shadow-lg', 'shadow-red-500/20');
}
});
function updateProgressBar() {
const progressPercent = ((currentStep - 1) / 2) * 100;
document.getElementById('wizard-progress-bar').style.width = `${progressPercent}%`;
// Reset indicator classes
for (let i = 1; i <= 3; i++) {
const dot = document.getElementById(`step-dot-${i}`);
const label = document.getElementById(`step-label-${i}`);
if (i < currentStep) {
// Completed Step
dot.className = "w-12 h-12 rounded-full bg-emerald-500 text-white flex items-center justify-center font-extrabold shadow-lg shadow-emerald-500/20 ring-4 ring-emerald-100 transition-all duration-500";
dot.innerHTML = `<i class="uil uil-check text-xl"></i>`;
label.className = "text-xs md:text-sm font-bold text-slate-800 mt-3";
} else if (i === currentStep) {
// Active Step
dot.className = "w-12 h-12 rounded-full bg-[#e31e24] text-white flex items-center justify-center font-extrabold shadow-lg shadow-red-500/20 ring-4 ring-red-100 transition-all duration-500";
if (i === 1) dot.innerHTML = `<i class="uil uil-info-circle text-xl"></i>`;
if (i === 2) dot.innerHTML = `<i class="uil uil-file-download text-xl"></i>`;
if (i === 3) dot.innerHTML = `<i class="uil uil-check-circle text-xl"></i>`;
label.className = "text-xs md:text-sm font-bold text-slate-800 mt-3";
} else {
// Pending Step
dot.className = "w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold ring-4 ring-transparent transition-all duration-500";
if (i === 2) dot.innerHTML = `<i class="uil uil-file-download text-xl"></i>`;
if (i === 3) dot.innerHTML = `<i class="uil uil-check-circle text-xl"></i>`;
label.className = "text-xs md:text-sm font-medium text-slate-400 mt-3";
}
}
}
function nextStep(step) {
if (step === 2 && !document.getElementById('terms-accept-checkbox').checked) {
return;
}
// Hide current step content with nice fade animation
const currentStepEl = document.getElementById(`step-content-${currentStep}`);
currentStepEl.classList.remove('opacity-100');
currentStepEl.classList.add('opacity-0');
setTimeout(() => {
currentStepEl.classList.add('hidden');
// Show next step content
const nextStepEl = document.getElementById(`step-content-${step}`);
nextStepEl.classList.remove('hidden');
// Force repaint to trigger animation
nextStepEl.offsetHeight;
nextStepEl.classList.remove('opacity-0');
nextStepEl.classList.add('opacity-100');
currentStep = step;
updateProgressBar();
window.scrollTo({ top: document.querySelector('.wrapper').offsetTop - 50, behavior: 'smooth' });
}, 200);
}
function prevStep(step) {
const currentStepEl = document.getElementById(`step-content-${currentStep}`);
currentStepEl.classList.remove('opacity-100');
currentStepEl.classList.add('opacity-0');
setTimeout(() => {
currentStepEl.classList.add('hidden');
const prevStepEl = document.getElementById(`step-content-${step}`);
prevStepEl.classList.remove('hidden');
prevStepEl.offsetHeight;
prevStepEl.classList.remove('opacity-0');
prevStepEl.classList.add('opacity-100');
currentStep = step;
updateProgressBar();
window.scrollTo({ top: document.querySelector('.wrapper').offsetTop - 50, behavior: 'smooth' });
}, 200);
}
</script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
@@ -337,6 +731,10 @@ document.addEventListener('DOMContentLoaded', function() {
confirmButtonText: '{{ __("career.ok") ?? "Tamam" }}'
}).then(() => {
form.reset();
// Go back to step 1
document.getElementById('terms-accept-checkbox').checked = false;
document.getElementById('terms-accept-checkbox').dispatchEvent(new Event('change'));
prevStep(1);
});
} else if (result.type === 'validation') {
Object.keys(result.errors).forEach(key => {
@@ -344,14 +742,14 @@ document.addEventListener('DOMContentLoaded', function() {
if (input) {
input.classList.add('is-invalid');
const errorDiv = document.createElement('div');
errorDiv.className = 'invalid-feedback text-start d-block';
errorDiv.className = 'invalid-feedback text-start text-xs text-red-500 mt-1 block';
errorDiv.textContent = result.errors[key][0];
// Handle different input structures
if (input.type === 'checkbox') {
input.closest('.space-y-4').appendChild(errorDiv);
input.closest('.flex').parentNode.appendChild(errorDiv);
} else {
input.closest('.md\\:col-span-9').appendChild(errorDiv);
input.parentNode.appendChild(errorDiv);
}
}
});
File diff suppressed because it is too large Load Diff
+441 -118
View File
@@ -12,133 +12,249 @@
<section class="wrapper bg-white py-12 lg:py-20">
<div class="container px-4">
<div class="text-center mb-12">
<h2 class="text-xs uppercase font-bold tracking-widest text-[#e31e24] mb-2">{{ __('career.remote_work_title', ['default' => 'Süreç Nasıl İşliyor?']) }}</h2>
<h3 class="text-3xl md:text-4xl font-extrabold text-slate-900">{{ __('career.remote_work_title', ['default' => 'Tamamen Uzaktan Staj Deneyimi']) }}</h3>
</div>
<div class="flex flex-col lg:flex-row items-center gap-12 mb-20 max-w-6xl mx-auto">
<div class="w-full lg:w-3/5">
<div class="bg-white p-4 md:p-8 rounded-3xl shadow-2xl border border-slate-100 overflow-hidden flex justify-center">
<pre class="mermaid">
graph TD
A[İnternet Üzerinden Başvuru] --> B{Uzaktan Çalışma Koşulları}
B -- Uygun --> C[CV ve Evrakların İletilmesi]
B -- Uygun Değil --> D[Süreç Sonlandırılır]
C --> E[Ön Değerlendirme]
E --> F[Teknik Görüşme]
F --> G[Projelere Dahil Edilme]
style A fill:#f8fafc,stroke:#cbd5e1,stroke-width:2px
style G fill:#e31e24,stroke:#fff,stroke-width:2px,color:#fff
style B fill:#fff1f2,stroke:#fecdd3
</pre>
<!-- Wizard Progress Tracker -->
<div class="max-w-md mx-auto mb-16">
<div class="relative flex items-center justify-between">
<!-- Progress Line -->
<div class="absolute left-0 top-1/2 -translate-y-1/2 w-full h-1 bg-slate-100 -z-10 rounded-full">
<div id="wizard-progress-bar" class="h-full bg-[#e31e24] rounded-full transition-all duration-500 w-[0%]"></div>
</div>
</div>
<div class="w-full lg:w-2/5 space-y-8">
<h4 class="text-2xl font-bold text-slate-900">{{ __('career.application_conditions_title', ['default' => 'Başvuru Şartları ve Çalışma Modeli']) }}</h4>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.daily_standup', ['default' => 'Günlük Standup:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.daily_standup_desc', ['default' => 'Her gün düzenlenen kısa toplantılarla ekip içi iletişim ve ilerleme takibi sağlanır.']) }}</p>
</div>
<!-- Step 1 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-1" class="w-12 h-12 rounded-full bg-[#e31e24] text-white flex items-center justify-center font-extrabold shadow-lg shadow-red-500/20 transition-all duration-500 ring-4 ring-red-100">
<i class="uil uil-info-circle text-xl"></i>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.flexible_projects', ['default' => 'Esnek Projeler:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.flexible_projects_desc', ['default' => 'Projelerimizde esneklik esastır. Yeteneklerinize uygun gerçek projelerde görev alırsınız.']) }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.focus_on_development', ['default' => 'Gelişim Odaklılık:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.condition_not_only_for_internship', ['default' => 'Yalnızca staj yapmak için yapmak isteyen kişiler alınmamaktadır.']) }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.intent_letter', ['default' => 'Niyet Mektubu:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.condition_intent_letter', ['default' => 'Mesaj bölümünde nasıl bir katkı sağlayacağınızın niyet mektubu iletilmelidir.']) }}</p>
</div>
<span id="step-label-1" class="text-xs md:text-sm font-bold text-slate-800 mt-3 transition-all duration-300">{{ __('career.intern_step_1_title') }}</span>
</div>
<!-- Step 2 Dot -->
<div class="flex flex-col items-center">
<div id="step-dot-2" class="w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold transition-all duration-500 ring-4 ring-transparent">
<i class="uil uil-check-circle text-xl"></i>
</div>
<span id="step-label-2" class="text-xs md:text-sm font-medium text-slate-400 mt-3 transition-all duration-300">{{ __('career.intern_step_2_title') }}</span>
</div>
</div>
</div>
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-[2rem] shadow-2xl overflow-hidden border border-slate-100">
<div class="p-8 md:p-12">
<div class="mb-10">
<h2 class="text-2xl font-bold text-slate-900 mb-2">{{ __('career.internship_title') }}</h2>
<div class="w-12 h-1 bg-[#e31e24] rounded-full"></div>
<!-- Main Form Container -->
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" id="career-application-form">
@csrf
<input type="hidden" name="type" value="internship">
<!-- ================= STEP 1: TERMS & CONDITIONS ================= -->
<div id="step-content-1" class="wizard-step transition-all duration-500 opacity-100 block">
<div class="max-w-5xl mx-auto mb-12">
<!-- Welcome Message -->
<div class="bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-2xl shadow-sm mb-12">
<h4 class="text-xl font-bold text-blue-900 mb-4">{{ __('career.dear_intern') }}</h4>
<div class="text-blue-800 space-y-4 leading-relaxed text-sm md:text-base">
<p>{!! __('career.intern_welcome_text_1') !!}</p>
<p>{!! __('career.intern_welcome_text_2') !!}</p>
</div>
</div>
<form action="{{ route('career.store') }}" method="POST" enctype="multipart/form-data" class="space-y-6" id="career-application-form">
@csrf
<input type="hidden" name="type" value="internship">
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="name" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.name', ['default' => 'Ad Soyad']) }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="text" name="name" id="name" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Örn: Ahmet Yılmaz" value="{{ old('name') }}" required>
<div class="text-center mb-12">
<h2 class="text-xs uppercase font-bold tracking-widest text-[#e31e24] mb-2">{{ __('career.remote_work_title', ['default' => 'Süreç Nasıl İşliyor?']) }}</h2>
<h3 class="text-3xl md:text-4xl font-extrabold text-slate-900">{{ __('career.remote_work_title', ['default' => 'Tamamen Uzaktan Staj Deneyimi']) }}</h3>
</div>
<div class="flex flex-col lg:flex-row gap-12 items-start mb-12">
<!-- Mermaid Flowchart Canvas -->
<div class="w-full lg:w-3/5">
<div id="mermaid-card" class="relative bg-white p-6 md:p-8 rounded-3xl shadow-xl border border-slate-100 overflow-x-auto flex flex-col items-center">
<!-- Fullscreen Toggle Button -->
<button type="button" onclick="toggleFullscreen()" class="absolute top-4 right-4 p-2.5 rounded-xl bg-slate-50 hover:bg-slate-100 border border-slate-100 hover:border-slate-200 text-slate-500 hover:text-slate-700 transition-all duration-200 shadow-sm flex items-center justify-center z-10" title="Tam Ekran / Fullscreen">
<i id="fullscreen-icon" class="uil uil-expand-arrows-alt text-lg"></i>
</button>
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest mb-4"><i class="uil uil-presentation-play mr-1"></i> {{ __('career.intern_flow_diagram') }}</span>
<div class="mermaid w-full flex justify-center" style="min-height: 400px;">
graph TD
%% Styles
classDef startNode fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#fff,font-weight:bold,rx:10px,ry:10px;
classDef decisionNode fill:#eab308,stroke:#ca8a04,stroke-width:2px,color:#1e293b,font-weight:bold;
classDef processNode fill:#10b981,stroke:#047857,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
classDef rejectNode fill:#ef4444,stroke:#b91c1c,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
classDef actionNode fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff,font-weight:bold,rx:5px,ry:5px;
Start["{{ __('career.intern_mermaid_start') }}"]:::startNode
Dec1{"{{ __('career.intern_mermaid_dec1') }}"}:::decisionNode
Red1["{{ __('career.intern_mermaid_red1') }}"]:::rejectNode
DocUpload["{{ __('career.intern_mermaid_doc_upload') }}"]:::processNode
DocSign["{{ __('career.intern_mermaid_doc_sign') }}"]:::processNode
GitAccess["{{ __('career.intern_mermaid_git') }}"]:::processNode
SampleTask["{{ __('career.intern_mermaid_sample') }}"]:::processNode
Dec2{"{{ __('career.intern_mermaid_dec2') }}"}:::decisionNode
Real["{{ __('career.intern_mermaid_real') }}"]:::processNode
Dec3{"{{ __('career.intern_mermaid_dec3') }}"}:::decisionNode
WaitTask["{{ __('career.intern_mermaid_wait') }}"]:::actionNode
Assign["{{ __('career.intern_mermaid_assign') }}"]:::processNode
WorkStart["{{ __('career.intern_mermaid_work') }}"]:::processNode
Review["{{ __('career.intern_mermaid_review') }}"]:::processNode
Dec4{"{{ __('career.intern_mermaid_dec4') }}"}:::decisionNode
Feedback["{{ __('career.intern_mermaid_feedback') }}"]:::actionNode
Success["{{ __('career.intern_mermaid_success') }}"]:::processNode
Graduation["{{ __('career.intern_mermaid_graduation') }}"]:::startNode
Start --> Dec1
Dec1 -- "Hayır" --> Red1
Dec1 -- "Evet" --> DocUpload
DocUpload --> DocSign
DocSign --> GitAccess
GitAccess --> Dec2
Dec2 -- "Hayır" --> SampleTask
SampleTask --> Dec2
Dec2 -- "Evet" --> Real
Real --> Dec3
Dec3 -- "Hayır" --> WaitTask
Dec3 -- "Evet" --> Assign
Assign --> WorkStart
WorkStart --> Review
Review --> Dec4
Dec4 -- "Hayır" --> Feedback
Feedback --> WorkStart
Dec4 -- "Evet" --> Success
Success --> Graduation
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="email" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.email', ['default' => 'E-posta']) }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="email" name="email" id="email" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Örn: ahmet@example.com" value="{{ old('email') }}" required>
<!-- Conditions & Model Requirements -->
<div class="w-full lg:w-2/5 space-y-8">
<h4 class="text-2xl font-bold text-slate-900">{{ __('career.application_conditions_title', ['default' => 'Başvuru Şartları ve Çalışma Modeli']) }}</h4>
<div class="space-y-6">
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.daily_standup', ['default' => 'Günlük Standup:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.daily_standup_desc', ['default' => 'Her gün düzenlenen kısa toplantılarla ekip içi iletişim ve ilerleme takibi sağlanır.']) }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.flexible_projects', ['default' => 'Esnek Projeler:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.flexible_projects_desc', ['default' => 'Projelerimizde esneklik esastır. Yeteneklerinize uygun gerçek projelerde görev alırsınız.']) }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.focus_on_development', ['default' => 'Gelişim Odaklılık:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.condition_not_only_for_internship', ['default' => 'Yalnızca staj yapmak için yapmak isteyen kişiler alınmamaktadır.']) }}</p>
</div>
</div>
<div class="flex items-start gap-4">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-50 flex items-center justify-center text-[#e31e24]">
<i class="uil uil-check-circle text-xl"></i>
</div>
<div>
<span class="font-bold text-slate-800 block text-sm">{{ __('career.intent_letter', ['default' => 'Niyet Mektubu:']) }}</span>
<p class="text-sm text-slate-500">{{ __('career.condition_intent_letter', ['default' => 'Mesaj bölümünde nasıl bir katkı sağlayacağınızın niyet mektubu iletilmelidir.']) }}</p>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="phone" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.phone', ['default' => 'Telefon']) }}</label>
<div class="md:col-span-9">
<input type="text" name="phone" id="phone" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="Örn: 05XX XXX XX XX" value="{{ old('phone') }}">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="cv" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.cv', ['default' => 'Özgeçmiş (CV)']) }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="cv" id="cv" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">PDF, DOC, DOCX (Maks. 5MB)</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-start">
<label for="message" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right pt-3">{{ __('career.message', ['default' => 'Mesajınız']) }}</label>
<div class="md:col-span-9">
<textarea name="message" id="message" class="w-full px-5 py-4 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300 min-h-[150px]" placeholder="Staj beklentileriniz ve çalışma alanlarınız hakkında bilgi verebilirsiniz...">{{ old('message') }}</textarea>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-start-4 md:col-span-9">
<button type="submit" id="submit-btn" class="w-full md:w-auto px-10 py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-full transition-all shadow-lg shadow-red-500/30 flex items-center justify-center gap-3">
<span class="btn-text">{{ __('career.submit', ['default' => 'Başvuruyu Gönder']) }}</span>
<span class="spinner-border spinner-border-sm hidden animate-spin w-4 h-4 border-2 border-white border-t-transparent rounded-full" role="status"></span>
<i class="uil uil-arrow-right text-xl btn-icon"></i>
<!-- Terms Acceptance & Next Trigger -->
<div class="bg-slate-50 p-6 rounded-2xl border border-slate-100 mt-8 space-y-4">
<div class="flex items-start gap-3">
<input type="checkbox" id="terms-accept-checkbox" class="w-5 h-5 rounded border-slate-300 text-[#e31e24] focus:ring-[#e31e24] mt-1 cursor-pointer">
<label for="terms-accept-checkbox" class="text-sm text-slate-700 font-semibold cursor-pointer">
{{ __('career.intern_terms_accept_label') }} <span class="text-[#e31e24]">*</span>
</label>
</div>
<button type="button" onclick="nextStep(2)" id="step-1-next-btn" class="w-full py-4 bg-slate-300 text-slate-500 font-bold rounded-xl transition-all flex items-center justify-center gap-2 cursor-not-allowed" disabled>
<span>{{ __('career.accept_intern_terms_btn') }}</span>
<i class="uil uil-arrow-right text-lg"></i>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- ================= STEP 2: COMPLETE APPLICATION ================= -->
<div id="step-content-2" class="wizard-step transition-all duration-500 opacity-0 hidden">
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-[2rem] shadow-2xl overflow-hidden border border-slate-100">
<div class="p-8 md:p-12">
<div class="mb-10 text-center">
<h2 class="text-3xl font-bold text-slate-900 mb-2">{{ __('career.form_title') }}</h2>
<p class="text-slate-500">{{ __('career.form_subtitle') }}</p>
<div class="w-12 h-1 bg-[#e31e24] rounded-full mx-auto mt-4"></div>
</div>
<div class="space-y-6">
<!-- Name -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="name" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.name') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="text" name="name" id="name" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="{{ __('career.name_placeholder') }}" value="{{ old('name') }}" required>
</div>
</div>
<!-- Email -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="email" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.email') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="email" name="email" id="email" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="{{ __('career.email_placeholder') }}" value="{{ old('email') }}" required>
</div>
</div>
<!-- Phone -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="phone" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.phone') }}</label>
<div class="md:col-span-9">
<input type="text" name="phone" id="phone" class="w-full px-5 py-3 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300" placeholder="{{ __('career.phone_placeholder') }}" value="{{ old('phone') }}">
</div>
</div>
<!-- CV Upload -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-center">
<label for="cv" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right">{{ __('career.cv') }} <span class="text-[#e31e24]">*</span></label>
<div class="md:col-span-9">
<input type="file" name="cv" id="cv" class="w-full px-5 py-2.5 rounded-xl border border-slate-200 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-red-50 file:text-[#e31e24] hover:file:bg-red-100 transition-all" required>
<p class="mt-2 text-xs text-slate-400">{{ __('career.cv_description') }}</p>
</div>
</div>
<!-- Message / Intent Letter -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 items-start">
<label for="message" class="md:col-span-3 text-sm font-bold text-slate-700 md:text-right pt-3">{{ __('career.message') }}</label>
<div class="md:col-span-9">
<textarea name="message" id="message" class="w-full px-5 py-4 rounded-xl border border-slate-200 focus:ring-4 focus:ring-red-500/10 focus:border-[#e31e24] outline-none transition-all placeholder-slate-300 min-h-[150px]" placeholder="Staj beklentileriniz ve çalışma alanlarınız hakkında bilgi verebilirsiniz...">{{ old('message') }}</textarea>
</div>
</div>
<!-- Step 2 Navigation -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-6 pt-6 border-t border-slate-100">
<div class="md:col-start-4 md:col-span-9 flex items-center justify-between gap-4">
<button type="button" onclick="prevStep(1)" class="px-6 py-4 bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold rounded-xl transition-all flex items-center gap-2">
<i class="uil uil-arrow-left text-lg"></i>
<span>{{ __('career.go_back') }}</span>
</button>
<button type="submit" id="submit-btn" class="flex-1 md:flex-none px-10 py-4 bg-[#e31e24] hover:bg-[#c4191f] text-white font-bold rounded-full transition-all shadow-lg shadow-red-500/30 flex items-center justify-center gap-3">
<span class="btn-text">{{ __('career.submit') }}</span>
<span class="spinner-border spinner-border-sm hidden animate-spin w-4 h-4 border-2 border-white border-t-transparent rounded-full" role="status"></span>
<i class="uil uil-arrow-right text-xl btn-icon"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</section>
@@ -200,6 +316,16 @@
outline: none;
}
input[type="checkbox"] {
-webkit-appearance: checkbox !important;
appearance: checkbox !important;
width: 1.25rem !important;
height: 1.25rem !important;
accent-color: var(--citrus-red);
cursor: pointer;
margin-top: 0;
}
.bg-soft-primary {
background: linear-gradient(135deg, #f0f7ff 0%, #e8f1ff 100%) !important;
}
@@ -238,22 +364,210 @@
padding-left: 2rem;
position: relative;
}
/* Mermaid diagram readability and aesthetics */
.mermaid .nodeLabel,
.mermaid .node text,
.mermaid text,
.mermaid .node .label {
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 0 1px rgba(0, 0, 0, 0.2) !important;
padding: 2px 8px !important;
}
.mermaid .node.decisionNode .nodeLabel,
.mermaid .node.decisionNode text,
.mermaid .node.decisionNode .label {
text-shadow: none !important;
color: #1e293b !important;
}
.mermaid svg {
filter: drop-shadow(0px 10px 20px rgba(15, 23, 42, 0.05));
}
/* Fullscreen styles for card container */
#mermaid-card:fullscreen,
#mermaid-card:-webkit-full-screen,
#mermaid-card:-ms-fullscreen {
background-color: #ffffff !important;
padding: 3rem 2rem !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
overflow: auto !important;
border-radius: 0px !important;
width: 100% !important;
height: 100% !important;
}
</style>
@endpush
@push('scripts')
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({
startOnLoad: true,
theme: 'default',
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.2.4/dist/mermaid.min.js"></script>
<script>
// Initialize Mermaid
mermaid.initialize({
startOnLoad: false,
theme: 'neutral',
securityLevel: 'loose',
flowchart: {
useMaxWidth: true,
htmlLabels: true,
curve: 'basis'
useMaxWidth: true,
htmlLabels: true
}
});
// Run mermaid after all fonts are fully loaded to ensure boxes match the text dimensions perfectly
if (document.fonts) {
document.fonts.ready.then(function() {
mermaid.run({
nodes: document.querySelectorAll('.mermaid')
});
});
} else {
window.addEventListener('load', function() {
mermaid.run({
nodes: document.querySelectorAll('.mermaid')
});
});
}
// Fullscreen toggle logic for Mermaid card
function toggleFullscreen() {
const card = document.getElementById('mermaid-card');
const icon = document.getElementById('fullscreen-icon');
if (!document.fullscreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
if (card.requestFullscreen) {
card.requestFullscreen();
} else if (card.webkitRequestFullscreen) { /* Safari */
card.webkitRequestFullscreen();
} else if (card.msRequestFullscreen) { /* IE11 */
card.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
}
// Handle icon sync on fullscreen change
function handleFullscreenChange() {
const icon = document.getElementById('fullscreen-icon');
if (document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
icon.className = "uil uil-shrink text-lg";
} else {
icon.className = "uil uil-expand-arrows-alt text-lg";
}
}
document.addEventListener('fullscreenchange', handleFullscreenChange);
document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
document.addEventListener('msfullscreenchange', handleFullscreenChange);
// Step variables
let currentStep = 1;
// Track Terms checkbox to enable Step 1 Next button
document.getElementById('terms-accept-checkbox').addEventListener('change', function(e) {
const nextBtn = document.getElementById('step-1-next-btn');
if (e.target.checked) {
nextBtn.disabled = false;
nextBtn.classList.remove('bg-slate-300', 'text-slate-500', 'cursor-not-allowed');
nextBtn.classList.add('bg-[#e31e24]', 'text-white', 'hover:bg-[#c4191f]', 'shadow-lg', 'shadow-red-500/20');
} else {
nextBtn.disabled = true;
nextBtn.classList.add('bg-slate-300', 'text-slate-500', 'cursor-not-allowed');
nextBtn.classList.remove('bg-[#e31e24]', 'text-white', 'hover:bg-[#c4191f]', 'shadow-lg', 'shadow-red-500/20');
}
});
function updateProgressBar() {
const progressPercent = ((currentStep - 1) / 1) * 100;
document.getElementById('wizard-progress-bar').style.width = `${progressPercent}%`;
// Reset indicator classes
for (let i = 1; i <= 2; i++) {
const dot = document.getElementById(`step-dot-${i}`);
const label = document.getElementById(`step-label-${i}`);
if (i < currentStep) {
// Completed Step
dot.className = "w-12 h-12 rounded-full bg-emerald-500 text-white flex items-center justify-center font-extrabold shadow-lg shadow-emerald-500/20 ring-4 ring-emerald-100 transition-all duration-500";
dot.innerHTML = `<i class="uil uil-check text-xl"></i>`;
label.className = "text-xs md:text-sm font-bold text-slate-800 mt-3";
} else if (i === currentStep) {
// Active Step
dot.className = "w-12 h-12 rounded-full bg-[#e31e24] text-white flex items-center justify-center font-extrabold shadow-lg shadow-red-500/20 ring-4 ring-red-100 transition-all duration-500";
if (i === 1) dot.innerHTML = `<i class="uil uil-info-circle text-xl"></i>`;
if (i === 2) dot.innerHTML = `<i class="uil uil-check-circle text-xl"></i>`;
label.className = "text-xs md:text-sm font-bold text-slate-800 mt-3";
} else {
// Pending Step
dot.className = "w-12 h-12 rounded-full bg-slate-100 text-slate-400 flex items-center justify-center font-extrabold ring-4 ring-transparent transition-all duration-500";
if (i === 2) dot.innerHTML = `<i class="uil uil-check-circle text-xl"></i>`;
label.className = "text-xs md:text-sm font-medium text-slate-400 mt-3";
}
}
}
function nextStep(step) {
if (step === 2 && !document.getElementById('terms-accept-checkbox').checked) {
return;
}
// Hide current step content with nice fade animation
const currentStepEl = document.getElementById(`step-content-${currentStep}`);
currentStepEl.classList.remove('opacity-100');
currentStepEl.classList.add('opacity-0');
setTimeout(() => {
currentStepEl.classList.add('hidden');
// Show next step content
const nextStepEl = document.getElementById(`step-content-${step}`);
nextStepEl.classList.remove('hidden');
// Force repaint to trigger animation
nextStepEl.offsetHeight;
nextStepEl.classList.remove('opacity-0');
nextStepEl.classList.add('opacity-100');
currentStep = step;
updateProgressBar();
window.scrollTo({ top: document.querySelector('.wrapper').offsetTop - 50, behavior: 'smooth' });
}, 200);
}
function prevStep(step) {
const currentStepEl = document.getElementById(`step-content-${currentStep}`);
currentStepEl.classList.remove('opacity-100');
currentStepEl.classList.add('opacity-0');
setTimeout(() => {
currentStepEl.classList.add('hidden');
const prevStepEl = document.getElementById(`step-content-${step}`);
prevStepEl.classList.remove('hidden');
prevStepEl.offsetHeight;
prevStepEl.classList.remove('opacity-0');
prevStepEl.classList.add('opacity-100');
currentStep = step;
updateProgressBar();
window.scrollTo({ top: document.querySelector('.wrapper').offsetTop - 50, behavior: 'smooth' });
}, 200);
}
</script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
@@ -306,6 +620,10 @@ document.addEventListener('DOMContentLoaded', function() {
confirmButtonText: '{{ __("career.ok") ?? "Tamam" }}'
}).then(() => {
form.reset();
// Go back to step 1
document.getElementById('terms-accept-checkbox').checked = false;
document.getElementById('terms-accept-checkbox').dispatchEvent(new Event('change'));
prevStep(1);
});
} else if (result.type === 'validation') {
Object.keys(result.errors).forEach(key => {
@@ -313,9 +631,14 @@ document.addEventListener('DOMContentLoaded', function() {
if (input) {
input.classList.add('is-invalid');
const errorDiv = document.createElement('div');
errorDiv.className = 'invalid-feedback text-start d-block';
errorDiv.className = 'invalid-feedback text-start text-xs text-red-500 mt-1 block';
errorDiv.textContent = result.errors[key][0];
input.closest('.md\\:col-span-9')?.appendChild(errorDiv) || input.closest('.col-md-9')?.appendChild(errorDiv);
if (input.type === 'checkbox') {
input.closest('.flex').parentNode.appendChild(errorDiv);
} else {
input.parentNode.appendChild(errorDiv);
}
}
});
@@ -0,0 +1,338 @@
<!-- Gantt Chart Card -->
<div class="bg-white rounded-3xl p-6 md:p-8 shadow-xl border border-slate-100/50 mb-8">
<div class="flex items-center justify-between mb-6 pb-4 border-b border-slate-100">
<h3 class="font-bold text-slate-800 text-lg flex items-center gap-2">
<i class="uil uil-schedule text-blue-600 text-xl"></i>
<span>Stajyer Gantt Şeması ve Takvimi</span>
</h3>
<span class="text-xs px-2.5 py-1 bg-blue-50 text-blue-600 rounded-full font-semibold">DevExtreme Görünümü</span>
</div>
@if($ganttInterns->isEmpty())
<div class="flex flex-col items-center justify-center p-8 text-center bg-slate-50 rounded-2xl border border-dashed border-slate-200">
<i class="uil uil-calendar-slash text-4xl text-slate-300 mb-2"></i>
<p class="text-sm font-medium text-slate-500">Tarihleri belirlenmiş onaylı stajyer bulunamadı.</p>
</div>
@else
<!-- Gantt Container -->
<div class="dx-viewport demo-container" style="height: 480px; overflow: hidden; border-radius: 16px; border: 1px solid rgba(0,0,0,0.05);">
<div id="gantt" style="height: 100%; width: 100%;"></div>
</div>
@endif
</div>
<!-- Dynamic Journal Modal -->
<div id="journal-modal" class="fixed inset-0 z-[9999] hidden items-center justify-center bg-slate-900/60 backdrop-blur-sm transition-opacity duration-300">
<div class="bg-white rounded-3xl shadow-2xl border border-slate-100 max-w-2xl w-full mx-4 overflow-hidden transform scale-95 opacity-0 transition-all duration-300 flex flex-col max-h-[90vh]">
<!-- Modal Header -->
<div class="px-6 py-4 bg-gradient-to-r from-blue-50 to-indigo-50/30 border-b border-slate-100 flex items-center justify-between">
<div>
<h4 id="modal-title" class="font-bold text-slate-800 text-lg">Staj Raporu Detayı</h4>
<p id="modal-subtitle" class="text-xs text-slate-500 font-semibold mt-0.5"></p>
</div>
<button type="button" onclick="closeJournalModal()" class="text-slate-400 hover:text-slate-600 transition-colors p-1.5 rounded-full hover:bg-slate-100/80">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
<!-- Modal Body -->
<div class="p-6 overflow-y-auto flex-grow prose max-w-none text-slate-700 leading-relaxed text-sm no-scrollbar">
<div id="modal-content" class="min-h-[100px] flex flex-col justify-center">
<!-- Content gets loaded here -->
</div>
</div>
<!-- Modal Footer -->
<div class="px-6 py-4 bg-slate-50/50 border-t border-slate-100 flex items-center justify-between">
<div id="modal-status-badge">
<!-- Status badge -->
</div>
<div class="flex gap-3">
<button type="button" onclick="closeJournalModal()" class="px-4 py-2 border border-slate-200 text-slate-600 hover:bg-slate-50 rounded-xl font-bold text-xs transition-colors">Kapat</button>
<button type="button" id="modal-action-btn" onclick="handleModalAction()" class="hidden px-4 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-xl font-bold text-xs shadow-lg shadow-blue-500/20 transition-all"></button>
</div>
</div>
</div>
</div>
@push('styles')
<!-- DevExtreme Gantt CSS Dependencies -->
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.fluent.blue.light.css">
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx-gantt.min.css">
<style>
.current-time-line {
background-color: rgba(239, 68, 68, 0.4) !important;
border-left: 2px dashed #ef4444 !important;
width: 2px !important;
}
.rich-text-content p { margin-bottom: 8px; }
.rich-text-content ul { list-style-type: disc !important; padding-left: 20px !important; margin-bottom: 8px !important; }
.rich-text-content ol { list-style-type: decimal !important; padding-left: 20px !important; margin-bottom: 8px !important; }
.rich-text-content li { margin-bottom: 4px !important; }
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
</style>
<script>
tailwind = {
config: {
corePlugins: {
preflight: false,
}
}
}
</script>
<script src="https://cdn.tailwindcss.com"></script>
@endpush
@push('scripts')
<!-- Gantt Chart Javascript Dependencies & Initialization -->
@if(!$ganttInterns->isEmpty())
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn3.devexpress.com/jslib/23.2.5/js/dx-gantt.min.js"></script>
<script src="https://cdn3.devexpress.com/jslib/23.2.5/js/dx.all.js"></script>
<script>
$(function() {
const ganttData = @json($ganttInterns);
const formattedData = ganttData.map(intern => ({
id: intern.id,
title: intern.title,
start: new Date(intern.start),
end: new Date(intern.end),
progress: 0
}));
let dayColumnsMap = {};
$('#gantt').dxGantt({
tasks: {
dataSource: formattedData,
},
editing: {
enabled: false,
},
validation: {
autoUpdateParentTasks: true,
},
toolbar: {
items: [
'collapseAll',
'expandAll',
'separator',
'zoomIn',
'zoomOut',
],
},
columns: [{
dataField: 'title',
caption: 'Stajyer Adı',
width: 200,
}, {
dataField: 'start',
caption: 'Başlangıç Tarihi',
dataType: 'date',
format: 'dd.MM.yyyy',
width: 110,
}, {
dataField: 'end',
caption: 'Bitiş Tarihi',
dataType: 'date',
format: 'dd.MM.yyyy',
width: 110,
}],
scaleType: 'days',
taskListWidth: 420,
stripLines: [{
title: 'Bugün',
start: new Date(),
cssClass: 'current-time-line'
}],
onScaleCellPrepared: function(e) {
if (e.scaleType === 'days') {
const $el = $(e.scaleElement || e.element);
const left = $el.position().left;
const width = $el.outerWidth();
const dateStr = e.startDate.toISOString().substring(0, 10);
dayColumnsMap[dateStr] = {
left: left,
right: left + width,
date: e.startDate,
dateStr: dateStr
};
}
}
});
// Grid task area cell click listener
$(document).on('click', '#gantt', function(e) {
const $target = $(e.target);
if ($target.closest('.dx-gantt-task-area').length > 0 || $target.closest('.dx-gantt-ts-area').length > 0) {
const ganttInstance = $('#gantt').dxGantt('instance');
const selectedKey = ganttInstance.option("selectedRowKey");
if (!selectedKey) {
alert("Lütfen önce sol taraftan stajyeri seçin, ardından tıklamak istediğiniz güne tıklayın.");
return;
}
const $taskArea = $('.dx-gantt-task-area').first();
if (!$taskArea.length) return;
const scrollLeft = ganttInstance._ganttView._taskAreaContainer.scrollLeft;
const clickX = e.pageX - $taskArea.offset().left;
const totalX = scrollLeft + clickX;
const cols = Object.values(dayColumnsMap);
const clickedCol = cols.find(col => totalX >= col.left && totalX <= col.right);
if (clickedCol) {
showDailyJournalPopup(selectedKey, clickedCol.dateStr);
}
}
});
});
let activeEntryId = null;
function showDailyJournalPopup(internId, dateStr) {
const modal = document.getElementById('journal-modal');
const contentDiv = document.getElementById('modal-content');
const titleH = document.getElementById('modal-title');
const subtitleP = document.getElementById('modal-subtitle');
const badgeDiv = document.getElementById('modal-status-badge');
const actionBtn = document.getElementById('modal-action-btn');
modal.classList.remove('hidden');
modal.classList.add('flex');
setTimeout(() => {
modal.firstElementChild.classList.remove('scale-95', 'opacity-0');
modal.firstElementChild.classList.add('scale-100', 'opacity-100');
}, 50);
contentDiv.innerHTML = `
<div class="flex items-center justify-center p-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
`;
titleH.textContent = "Yükleniyor...";
subtitleP.textContent = "";
badgeDiv.innerHTML = "";
actionBtn.classList.add('hidden');
activeEntryId = null;
fetch(`/stajyer/admin/journal-entry?intern_id=${internId}&date=${dateStr}`)
.then(res => res.json())
.then(data => {
if (!data.success) {
contentDiv.innerHTML = `<p class="text-slate-500 italic text-center p-8">${data.message}</p>`;
titleH.textContent = "Bilgi";
return;
}
titleH.textContent = `${data.intern_name}`;
subtitleP.textContent = `${data.day_number}. Gün Raporu — ${data.date_formatted}`;
if (!data.entry) {
contentDiv.innerHTML = `
<div class="flex flex-col items-center justify-center p-8 text-center bg-slate-50 rounded-2xl border border-dashed border-slate-200">
<i class="uil uil-file-slash text-3xl text-slate-400 mb-2"></i>
<p class="text-sm font-medium text-slate-500">Bu gün için henüz staj raporu yazılmamıştır.</p>
</div>
`;
return;
}
activeEntryId = data.entry.id;
const entryContent = data.entry.content || '<p class="text-slate-400 italic">Boş içerik.</p>';
let contentHtml = '';
if (data.entry.is_retroactive) {
contentHtml += `
<div class="mb-4 inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-rose-50 text-rose-700 text-[10px] font-extrabold uppercase border border-rose-100">
<span class="w-1.5 h-1.5 rounded-full bg-rose-500 animate-pulse"></span>
Geriye Dönük Kayıt
</div>
`;
}
contentHtml += `<div class="rich-text-content prose max-w-none text-slate-700 leading-relaxed text-sm select-text">${entryContent}</div>`;
contentDiv.innerHTML = contentHtml;
updateModalStatus(data.entry.supervisor_approved, data.entry.supervisor_name);
})
.catch(err => {
console.error(err);
contentDiv.innerHTML = `<p class="text-red-500 text-center p-8 font-semibold">Veriler yüklenirken bir hata oluştu.</p>`;
titleH.textContent = "Hata";
});
}
function updateModalStatus(approved, name) {
const badgeDiv = document.getElementById('modal-status-badge');
const actionBtn = document.getElementById('modal-action-btn');
actionBtn.classList.remove('hidden');
if (approved) {
badgeDiv.innerHTML = `
<span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-emerald-50 text-emerald-700 text-xs font-bold border border-emerald-100">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
Sorumlu Onayladı ${name ? `(${name})` : ''}
</span>
`;
actionBtn.textContent = "Onayı Kaldır";
actionBtn.className = "px-4 py-2 text-white bg-rose-600 hover:bg-rose-700 rounded-xl font-bold text-xs shadow-lg shadow-rose-500/20 transition-all cursor-pointer";
} else {
badgeDiv.innerHTML = `
<span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-slate-100 text-slate-600 text-xs font-bold border border-slate-200">
Onay Bekliyor
</span>
`;
actionBtn.textContent = "Raporu Onayla";
actionBtn.className = "px-4 py-2 text-white bg-emerald-600 hover:bg-emerald-700 rounded-xl font-bold text-xs shadow-lg shadow-emerald-500/20 transition-all cursor-pointer";
}
}
function handleModalAction() {
if (!activeEntryId) return;
const actionBtn = document.getElementById('modal-action-btn');
actionBtn.disabled = true;
actionBtn.style.opacity = "0.5";
fetch('/stajyer/admin/toggle-approval', {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": "{{ csrf_token() }}"
},
body: JSON.stringify({
entry_id: activeEntryId
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
updateModalStatus(data.status, data.supervisor_name);
} else {
alert(data.message || "Onay güncellenemedi.");
}
})
.catch(err => {
console.error(err);
alert("İşlem sırasında bir hata oluştu.");
})
.finally(() => {
actionBtn.disabled = false;
actionBtn.style.opacity = "1";
});
}
function closeJournalModal() {
const modal = document.getElementById('journal-modal');
modal.firstElementChild.classList.add('scale-95', 'opacity-0');
modal.firstElementChild.classList.remove('scale-100', 'opacity-100');
setTimeout(() => {
modal.classList.add('hidden');
modal.classList.remove('flex');
}, 200);
}
</script>
@endif
@endpush
@@ -0,0 +1,132 @@
<!-- Stajyer Rehberi Modal -->
<div id="guideModal" class="fixed inset-0 z-[9999] hidden flex items-center justify-center p-4">
<!-- Backdrop -->
<div class="absolute inset-0 bg-slate-900/60 backdrop-blur-md transition-opacity duration-300" onclick="toggleGuideModal(false)"></div>
<!-- Modal Content Card -->
<div class="relative bg-white rounded-3xl w-full max-w-2xl max-h-[85vh] overflow-y-auto shadow-2xl border border-slate-100/50 transform scale-95 opacity-0 transition-all duration-300 flex flex-col" id="guideModalCard">
<!-- Modal Header -->
<div class="sticky top-0 bg-white z-10 px-6 py-5 border-b border-slate-100 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-red-50 text-[#e31e24] flex items-center justify-center text-xl">
<i class="uil uil-book-open"></i>
</div>
<div>
<h3 class="font-bold text-slate-800 text-lg text-white">Stajyer Çalışma & Git Workflow Rehberi</h3>
<p class="text-xs text-slate-400 font-semibold mt-0.5">Başarılı bir staj süreci için uymanız gereken kurallar</p>
</div>
</div>
<button onclick="toggleGuideModal(false)" class="w-8 h-8 rounded-lg bg-slate-50 hover:bg-slate-100 text-slate-400 hover:text-slate-600 flex items-center justify-center transition-colors">
<i class="uil uil-times text-xl"></i>
</button>
</div>
<!-- Modal Body -->
<div class="p-6 md:p-8 space-y-6">
<!-- Bullet 1: GitHub Public Repo -->
<div class="flex gap-4 items-start">
<div class="w-8 h-8 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center flex-shrink-0 mt-1 font-bold text-xs">
1
</div>
<div>
<h4 class="font-extrabold text-slate-800 text-sm">GitHub Public Repo Oluşturma</h4>
<p class="text-xs text-slate-500 mt-1 leading-relaxed">
Staj süresince geliştireceğiniz projeler için GitHub üzerinde <strong>public (herkese açık)</strong> bir repository oluşturmanız ve bu repo bağlantısını stajyer panelinize kaydetmeniz gerekmektedir.
</p>
</div>
</div>
<!-- Bullet 2: Implementation Plan -->
<div class="flex gap-4 items-start">
<div class="w-8 h-8 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center flex-shrink-0 mt-1 font-bold text-xs">
2
</div>
<div>
<h4 class="font-extrabold text-slate-800 text-sm">İlk Adım: Implementation Plan</h4>
<p class="text-xs text-slate-500 mt-1 leading-relaxed">
Deponuzu oluşturduktan sonra ilk commit olarak ana dizinde <code>implementation_plan.md</code> adında bir dosya hazırlamalısınız. Bu plan, projenizin mimarisini, hedeflerinizi ve yapacağınız işlerin genel haritasını içermelidir.
</p>
</div>
</div>
<!-- Bullet 3: Daily Commits & Intern Journal -->
<div class="flex gap-4 items-start">
<div class="w-8 h-8 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center flex-shrink-0 mt-1 font-bold text-xs">
3
</div>
<div>
<h4 class="font-extrabold text-slate-800 text-sm">Günlük Commit Zorunluluğu (Staj Defteri)</h4>
<p class="text-xs text-slate-500 mt-1 leading-relaxed">
Projeyi geliştirirken yaptığınız günlük commitler otomatik olarak staj günlüğünüze işlenir ve staj defterinizin içeriğini oluşturur. <strong>Eğer o gün commit gönderilmemişse, o gün çalışılmamış sayılacaktır.</strong>
</p>
</div>
</div>
<!-- Bullet 4: Daily Standup Meetings -->
<div class="flex gap-4 items-start">
<div class="w-8 h-8 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center flex-shrink-0 mt-1 font-bold text-xs">
4
</div>
<div>
<h4 class="font-extrabold text-slate-800 text-sm">Daily Standup Toplantıları</h4>
<p class="text-xs text-slate-500 mt-1 leading-relaxed">
Her iş günü düzenli olarak daily standup (günlük durum değerlendirme) toplantısı yapılacaktır. Toplantılarda dünün özeti, bugünün planları ve karşılaşılan engeller paylaşılır. <strong>Tüm stajyerlerin katılımı zorunludur.</strong>
</p>
</div>
</div>
<!-- Bullet 5: Git Workflow & Commit Culture -->
<div class="flex gap-4 items-start">
<div class="w-8 h-8 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center flex-shrink-0 mt-1 font-bold text-xs">
5
</div>
<div>
<h4 class="font-extrabold text-slate-800 text-sm">Git Workflow & Temiz Kod Kültürü</h4>
<p class="text-xs text-slate-500 mt-1 leading-relaxed">
Anlamlı commit mesajları yazmaya özen gösterin (örn. <code>feat: add login validation</code>). Kodlarınızı mantıksal parçalara ayırarak commit atın ve Git versiyon kontrol standartlarına uyun.
</p>
</div>
</div>
</div>
<!-- Modal Footer -->
<div class="sticky bottom-0 bg-slate-50 z-10 px-6 py-4 border-t border-slate-100/50 flex items-center justify-end gap-3 rounded-b-3xl">
<button onclick="toggleGuideModal(false)" class="px-5 py-2.5 bg-slate-850 hover:bg-slate-900 text-white font-extrabold rounded-xl text-xs transition-colors shadow-md shadow-slate-800/10">
Anladım, Kapat
</button>
</div>
</div>
</div>
<script>
function toggleGuideModal(show) {
const modal = document.getElementById('guideModal');
const card = document.getElementById('guideModalCard');
if (!modal || !card) return;
if (show) {
modal.classList.remove('hidden');
setTimeout(() => {
card.classList.remove('scale-95', 'opacity-0');
card.classList.add('scale-100', 'opacity-100');
}, 10);
} else {
card.classList.remove('scale-100', 'opacity-100');
card.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.classList.add('hidden');
}, 300);
}
}
// Handle ESC key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
toggleGuideModal(false);
}
});
</script>
@@ -0,0 +1,139 @@
<!-- Interns Table & Management List -->
<div class="bg-white rounded-3xl p-6 md:p-8 shadow-xl border border-slate-100/50">
<h3 class="font-bold text-slate-800 text-lg mb-6 pb-4 border-b border-slate-100 flex items-center gap-2">
<i class="uil uil-list-ul text-[#e31e24] text-xl"></i>
<span>Stajyer Listesi ve Detaylı Takip</span>
</h3>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="border-b border-slate-100 text-xs font-extrabold uppercase tracking-wider text-slate-400">
<th class="pb-4">Stajyer</th>
<th class="pb-4">Staj Tarihleri</th>
<th class="pb-4">Defter İlerlemesi</th>
<th class="pb-4">Durum</th>
<th class="pb-4">Belgeler</th>
<th class="pb-4">GitHub & Repo</th>
<th class="pb-4">Kayıt Tarihi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@foreach($allInterns as $intern)
<tr class="hover:bg-slate-50/50 transition-colors">
<!-- Name & Info -->
<td class="py-4">
<div class="font-bold text-slate-800 text-sm cursor-pointer hover:underline hover:text-blue-600 flex items-center gap-1.5" onclick="openInternJournalModal({{ $intern->id }})">
<span>{{ $intern->name }}</span>
<span class="inline-flex px-1.5 py-0.5 rounded-md bg-blue-50 text-blue-600 text-[9px] font-extrabold uppercase border border-blue-100 tracking-wider">Defteri İncele</span>
</div>
<div class="text-xs text-slate-400 mt-0.5 flex flex-col gap-0.5">
<span><i class="uil uil-envelope mr-1"></i>{{ $intern->email }}</span>
@if($intern->phone)
<span><i class="uil uil-phone mr-1"></i>{{ $intern->phone }}</span>
@endif
</div>
</td>
<!-- Internship Dates -->
<td class="py-4 text-xs font-semibold text-slate-600">
@if($intern->internship_start_date && $intern->internship_end_date)
<div class="flex flex-col">
<span>{{ \Carbon\Carbon::parse($intern->internship_start_date)->format('d.m.Y') }} - {{ \Carbon\Carbon::parse($intern->internship_end_date)->format('d.m.Y') }}</span>
<span class="text-blue-500 font-extrabold mt-0.5">{{ $intern->internship_total_days }} İş Günü</span>
</div>
@else
<span class="text-slate-300 italic">Belirlenmemiş</span>
@endif
</td>
<!-- Defter İlerlemesi -->
<td class="py-4">
@if($intern->internship_total_days)
@php
$filledDays = $intern->journalEntries->filter(function($entry) {
return !empty(trim($entry->content));
})->count();
$progressPercent = ($filledDays / $intern->internship_total_days) * 100;
if ($progressPercent > 100) $progressPercent = 100;
@endphp
<div class="flex flex-col gap-1 max-w-[130px]">
<div class="flex justify-between text-[10px] font-extrabold">
<span class="text-slate-400">Doldurulan</span>
<span class="text-blue-600">{{ $filledDays }} / {{ $intern->internship_total_days }} Gün</span>
</div>
<div class="w-full bg-slate-100 h-2 rounded-full overflow-hidden border border-slate-200/40">
<div class="bg-blue-600 h-full rounded-full transition-all duration-300" style="width: {{ $progressPercent }}%"></div>
</div>
</div>
@else
<span class="text-slate-300 italic text-xs">Belirlenmemiş</span>
@endif
</td>
<!-- Status -->
<td class="py-4">
<span class="inline-flex px-2.5 py-1 rounded-full text-[10px] font-extrabold uppercase tracking-wider
@if($intern->status === 'accepted') bg-green-50 text-green-700 border border-green-200
@elseif($intern->status === 'rejected') bg-red-50 text-red-700 border border-red-200
@elseif($intern->status === 'reviewed') bg-blue-50 text-blue-700 border border-blue-200
@elseif($intern->status === 'waiting_document') bg-amber-50 text-amber-700 border border-amber-200
@else bg-slate-50 text-slate-500 border border-slate-200 @endif">
@if($intern->status === 'accepted') Kabul Edildi
@elseif($intern->status === 'rejected') Reddedildi
@elseif($intern->status === 'reviewed') İncelendi
@elseif($intern->status === 'waiting_document') Evrak Bekleniyor
@else Beklemede @endif
</span>
</td>
<!-- Documents -->
<td class="py-4 space-y-1">
<div class="flex flex-wrap gap-1">
@if($intern->cv_path)
<a href="{{ Storage::disk('public')->url($intern->cv_path) }}" target="_blank" class="px-2 py-1 bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold text-[10px] rounded-lg transition-all flex items-center gap-1">
<i class="uil uil-file-alt"></i> CV
</a>
@endif
@if($intern->to_be_signed_internship_form_path)
<a href="{{ Storage::disk('public')->url($intern->to_be_signed_internship_form_path) }}" target="_blank" class="px-2 py-1 bg-blue-50 hover:bg-blue-100 text-blue-600 font-bold text-[10px] rounded-lg transition-all flex items-center gap-1">
<i class="uil uil-file-upload"></i> İmzalanacak Form
</a>
@endif
@if($intern->signed_internship_form_path)
<a href="{{ Storage::disk('public')->url($intern->signed_internship_form_path) }}" target="_blank" class="px-2 py-1 bg-green-50 hover:bg-green-100 text-green-600 font-bold text-[10px] rounded-lg transition-all flex items-center gap-1">
<i class="uil uil-file-check-alt"></i> İmzalı Form
</a>
@endif
</div>
</td>
<!-- GitHub Repository -->
<td class="py-4">
@if($intern->github_repo)
<div class="flex flex-col gap-1">
<a href="{{ $intern->github_repo }}" target="_blank" class="text-blue-600 hover:text-blue-800 font-extrabold text-xs inline-flex items-center gap-1">
<i class="uil uil-github"></i> Depoyu Aç
</a>
<!-- If they have repo, we can fetch their journal commits or trigger the controller download markdown -->
<a href="{{ route('intern.download-journal') }}?intern_id={{ $intern->id }}" class="px-2 py-1 bg-emerald-50 hover:bg-emerald-100 text-emerald-600 font-bold text-[10px] rounded-lg transition-all flex items-center justify-center gap-1 w-max">
<i class="uil uil-arrow-down-tray"></i> Günlüğü İndir (.md)
</a>
</div>
@else
<span class="text-slate-300 italic">Tanımlanmamış</span>
@endif
</td>
<!-- Register Date -->
<td class="py-4 text-xs font-semibold text-slate-400">
{{ \Carbon\Carbon::parse($intern->created_at)->format('d.m.Y H:i') }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@@ -0,0 +1,42 @@
<!-- Stats Grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="bg-white p-6 rounded-3xl border border-slate-100 shadow-lg flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-blue-50 text-blue-600 flex items-center justify-center text-2xl">
<i class="uil uil-users-alt"></i>
</div>
<div>
<span class="text-xs font-bold text-slate-400 block uppercase">Toplam Stajyer</span>
<span class="text-2xl font-extrabold text-slate-800">{{ $allInterns->count() }}</span>
</div>
</div>
<div class="bg-white p-6 rounded-3xl border border-slate-100 shadow-lg flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-emerald-50 text-emerald-600 flex items-center justify-center text-2xl">
<i class="uil uil-check-circle"></i>
</div>
<div>
<span class="text-xs font-bold text-slate-400 block uppercase">Kabul Edilen</span>
<span class="text-2xl font-extrabold text-slate-800">{{ $allInterns->where('status', 'accepted')->count() }}</span>
</div>
</div>
<div class="bg-white p-6 rounded-3xl border border-slate-100 shadow-lg flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-amber-50 text-amber-600 flex items-center justify-center text-2xl">
<i class="uil uil-hourglass-half"></i>
</div>
<div>
<span class="text-xs font-bold text-slate-400 block uppercase">Evrak Bekleyen</span>
<span class="text-2xl font-extrabold text-slate-800">{{ $allInterns->where('status', 'waiting_document')->count() }}</span>
</div>
</div>
<div class="bg-white p-6 rounded-3xl border border-slate-100 shadow-lg flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-indigo-50 text-indigo-600 flex items-center justify-center text-2xl">
<i class="uil uil-github-alt"></i>
</div>
<div>
<span class="text-xs font-bold text-slate-400 block uppercase">Git Tanımlayan</span>
<span class="text-2xl font-extrabold text-slate-800">{{ $allInterns->whereNotNull('github_repo')->count() }}</span>
</div>
</div>
</div>
@@ -0,0 +1,25 @@
<!-- Top header / Welcome card -->
<div class="bg-white rounded-3xl p-6 md:p-8 shadow-xl border border-slate-100/50 flex flex-col md:flex-row justify-between items-start md:items-center gap-6 mb-8">
<div>
<span class="text-xs uppercase font-extrabold tracking-widest text-[#e31e24] bg-red-50 px-3 py-1.5 rounded-full">Yönetici Paneli</span>
<h1 class="text-3xl font-extrabold text-slate-900 tracking-tight mt-3">Hoş Geldiniz, Yöneticimiz</h1>
<p class="text-sm text-slate-500 mt-1">Stajyer başvurularını, takvimlerini ve proje depolarını buradan takip edebilirsiniz.</p>
</div>
<div class="flex gap-3 flex-wrap">
<button onclick="toggleGuideModal(true)" class="px-5 py-3 rounded-xl bg-blue-50 hover:bg-blue-100 text-blue-600 font-extrabold text-sm transition-all flex items-center gap-2 border border-blue-100">
<i class="uil uil-book-open text-lg"></i>
<span>Stajyer Rehberi</span>
</button>
<a href="{{ route('homepage') }}" class="px-5 py-3 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold text-sm transition-all flex items-center gap-2 border border-slate-200/50">
<i class="uil uil-home text-lg"></i>
<span>Ana Sayfa</span>
</a>
<form action="{{ route('intern.admin.logout') }}" method="POST">
@csrf
<button type="submit" class="px-5 py-3 rounded-xl bg-red-50 hover:bg-red-100 text-[#e31e24] font-bold text-sm transition-all flex items-center gap-2 border border-red-100">
<i class="uil uil-sign-out-alt text-lg"></i>
<span>Çıkış Yap</span>
</button>
</form>
</div>
</div>
@@ -0,0 +1,442 @@
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Staj Defteri - {{ $intern->name }}</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Outfit', 'Segoe UI', sans-serif;
color: #1e293b;
background-color: #f8fafc;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
/* Page setup depending on chosen size */
@page {
@if($size === 'a5')
size: A5 portrait;
margin: 10mm;
@else
size: A4 portrait;
margin: 15mm;
@endif
}
.notebook-page {
background-color: #ffffff;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
page-break-after: always;
break-after: page;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
@if($size === 'a5')
width: 148mm;
height: 210mm;
padding: 12mm;
font-size: 11px;
@else
width: 210mm;
height: 297mm;
padding: 20mm;
font-size: 13px;
@endif
margin: 20px auto;
}
/* Adjustments for actual print mode */
@media print {
body {
background-color: #ffffff;
}
.notebook-page {
border: none;
box-shadow: none;
margin: 0;
width: 100%;
height: 100%;
}
.no-print {
display: none !important;
}
}
/* Top control bar */
.control-bar {
background: #ffffff;
border-bottom: 1px solid #e2e8f0;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
cursor: pointer;
transition: all 0.2s;
border: 1px solid transparent;
}
.btn-primary {
background-color: #2563eb;
color: #ffffff;
}
.btn-primary:hover {
background-color: #1d4ed8;
}
.btn-secondary {
background-color: #f1f5f9;
color: #475569;
border-color: #cbd5e1;
}
.btn-secondary:hover {
background-color: #e2e8f0;
}
.control-title {
font-weight: 800;
font-size: 16px;
color: #0f172a;
}
.control-actions {
display: flex;
gap: 10px;
}
/* Page Layout Styles */
.header-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 15px;
}
.header-table td {
border: 1px solid #cbd5e1;
padding: 8px 12px;
vertical-align: middle;
}
.logo-cell {
width: 150px;
text-align: center;
}
.logo-img {
max-height: 40px;
max-width: 100%;
}
.company-title {
font-weight: 800;
font-size: 12px;
color: #0f172a;
line-height: 1.3;
text-transform: uppercase;
}
.doc-title {
font-weight: 800;
color: #2563eb;
text-align: right;
font-size: 14px;
}
.meta-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.meta-table td {
border: 1px solid #cbd5e1;
padding: 6px 10px;
font-size: 11px;
}
.meta-label {
font-weight: 700;
color: #475569;
background-color: #f8fafc;
width: 25%;
}
.meta-value {
font-weight: 600;
color: #0f172a;
}
.day-banner {
background: linear-gradient(135deg, #eff6ff, #dbeafe);
border: 1px solid #bfdbfe;
border-radius: 8px;
padding: 10px 15px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.day-badge {
font-weight: 800;
color: #1d4ed8;
font-size: 14px;
}
.day-date {
font-weight: 700;
color: #475569;
font-size: 12px;
}
.content-area {
flex-grow: 1;
border: 1px solid #cbd5e1;
border-radius: 8px;
padding: 20px;
background: #ffffff;
margin-bottom: 20px;
min-height: 250px;
line-height: 1.6;
color: #334155;
font-size: 13px;
}
.content-area ul {
list-style-type: disc !important;
padding-left: 20px !important;
margin-top: 8px !important;
margin-bottom: 8px !important;
}
.content-area ol {
list-style-type: decimal !important;
padding-left: 20px !important;
margin-top: 8px !important;
margin-bottom: 8px !important;
}
.content-area li {
margin-bottom: 4px !important;
}
.empty-content {
color: #94a3b8;
font-style: italic;
text-align: center;
padding-top: 50px;
}
.footer-section {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: auto;
border-t: 1px solid #e2e8f0;
padding-top: 15px;
}
.qr-block {
display: flex;
align-items: center;
gap: 10px;
}
.qr-img {
width: 100px;
height: 100px;
border: 1px solid #e2e8f0;
padding: 2px;
border-radius: 4px;
}
.qr-text {
font-size: 9px;
color: #64748b;
max-width: 120px;
line-height: 1.3;
font-weight: 500;
}
.signatures-container {
display: flex;
gap: 20px;
}
.signature-box {
border: 1px solid #cbd5e1;
border-radius: 6px;
padding: 8px 12px;
width: 150px;
text-align: center;
font-size: 9px;
background: #f8fafc;
min-height: 70px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.sig-title {
font-weight: 700;
color: #475569;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 4px;
margin-bottom: 4px;
text-transform: uppercase;
}
.sig-status {
font-weight: 800;
color: #059669;
margin: 6px 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
.sig-status.waiting {
color: #d97706;
}
.sig-badge {
background-color: #d1fae5;
color: #065f46;
padding: 1px 6px;
border-radius: 4px;
font-size: 7px;
font-weight: 800;
display: inline-block;
margin-top: 2px;
}
.sig-badge.waiting {
background-color: #fef3c7;
color: #92400e;
}
.sig-name {
font-weight: 700;
color: #1e293b;
}
</style>
</head>
<body>
<!-- Control Bar -->
<div class="control-bar no-print">
<div class="control-title">Staj Defteri Önizleme ({{ $size === 'a5' ? 'A5 Boyutu' : 'A4 Boyutu' }})</div>
<div class="control-actions">
<a href="{{ route('intern.dashboard') }}" class="btn btn-secondary">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
<span>Panele Dön</span>
</a>
<div style="border-left: 1px solid #e2e8f0; margin: 0 5px; height: 35px;"></div>
<a href="?size=a4{{ request()->has('intern_id') ? '&intern_id='.request('intern_id') : '' }}{{ request()->has('day') ? '&day='.request('day') : '' }}" class="btn {{ $size === 'a4' ? 'btn-primary' : 'btn-secondary' }}">A4 Boyutu</a>
<a href="?size=a5{{ request()->has('intern_id') ? '&intern_id='.request('intern_id') : '' }}{{ request()->has('day') ? '&day='.request('day') : '' }}" class="btn {{ $size === 'a5' ? 'btn-primary' : 'btn-secondary' }}">A5 Boyutu</a>
<button onclick="window.print()" class="btn btn-primary">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
<span>Yazdır / PDF Kaydet</span>
</button>
</div>
</div>
<!-- Render Each Day -->
@foreach($days as $day)
@php
$entry = $savedEntries->get($day['day_number']);
$dayNum = $day['day_number'];
$dayDate = $day['date'];
$dayDateFormatted = $day['formatted_date'];
// Build the verification QR code URL pointing to the verification page
$verifyUrl = route('internship.verify', $intern->certificate_code) . '?day=' . $dayNum;
$qrCodeUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=' . urlencode($verifyUrl);
@endphp
<div class="notebook-page">
<div>
<!-- Header Card -->
<table class="header-table">
<tr>
<td class="logo-cell">
<img src="{{ asset('assets/img/truncgil-yatay.svg') }}" class="logo-img" alt="Logo">
</td>
<td>
<div class="company-title">TRUNÇGİL TEKNOLOJİ</div>
<div style="font-size: 8px; color: #64748b; margin-top: 2px;">Gaziantep Teknopark, Şahinbey / Gaziantep</div>
</td>
<td style="text-align: right;">
<div class="doc-title">STAJ GÜNLÜK RAPORU</div>
<div style="font-size: 8px; color: #64748b; margin-top: 2px;">Staj Defteri Sayfası</div>
</td>
</tr>
</table>
<!-- Student & Metadata Grid -->
<table class="meta-table">
<tr>
<td class="meta-label">Öğrenci Ad Soyad</td>
<td class="meta-value">{{ $intern->name }}</td>
<td class="meta-label">Staj Türü / Deposu</td>
<td class="meta-value">{{ $intern->github_repo ? 'GitHub Projeli Staj' : 'Genel Staj' }}</td>
</tr>
<tr>
<td class="meta-label">Staj Başlangıç</td>
<td class="meta-value">{{ \Carbon\Carbon::parse($intern->internship_start_date)->format('d.m.Y') }}</td>
<td class="meta-label">Toplam İş Günü</td>
<td class="meta-value">{{ $intern->internship_total_days }} Gün</td>
</tr>
</table>
<!-- Day Header Banner -->
<div class="day-banner">
<span class="day-badge">{{ $dayNum }}. Gün Raporu</span>
<span class="day-date"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block; vertical-align:middle; margin-right:4px;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>{{ $dayDateFormatted }}</span>
</div>
<!-- Content Area -->
<div class="content-area">
@if($entry && trim($entry->content))
{!! $entry->content !!}
@else
<div class="empty-content">Bu gün için herhangi bir staj raporu girilmemiştir.</div>
@endif
</div>
</div>
<!-- Footer & Signatures Block -->
<div class="footer-section">
<!-- QR Code Block -->
<div class="qr-block">
<img src="{{ $qrCodeUrl }}" class="qr-img" alt="QR Code">
<div class="qr-text">
<strong>KOD DOĞRULAMA</strong><br>
Bu staj sayfası webden doğrulanabilir. Doğrulamak için QR kodu taratın.
</div>
</div>
<!-- Signatures -->
<div class="signatures-container">
<!-- Supervisor -->
<div class="signature-box">
<div class="sig-title">Staj Sorumlusu</div>
@if($entry && $entry->supervisor_approved)
<div class="sig-status">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="color: #059669;"><polyline points="20 6 9 17 4 12"/></svg>
<span class="sig-badge">E-ONAYLI</span>
</div>
<div class="sig-name">{{ $entry->supervisor_name ?: ($intern->notebook_supervisor_name ?: 'Alperen Trunç') }}</div>
@else
<div class="sig-status waiting">
<span class="sig-badge waiting">İMZA BEKLENİYOR</span>
</div>
<div class="sig-name">&nbsp;</div>
@endif
</div>
</div>
</div>
</div>
@endforeach
</body>
</html>
@@ -0,0 +1,679 @@
@extends('layouts.site')
@section('content')
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700;800;900&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,500;0,700;1,400&display=swap" rel="stylesheet">
@if($application->status === 'accepted' && $application->notebook_approved)
<div class="bg-slate-100 py-12 no-print">
<div class="container max-w-6xl mx-auto px-4">
<!-- Verification Banner (e-Devlet Style) -->
<div class="bg-emerald-50 border-l-4 border-emerald-500 p-4 rounded-xl shadow-sm mb-6 flex flex-col lg:flex-row lg:items-center justify-between gap-4">
<div class="flex items-center gap-3 flex-1 min-w-0">
<div class="w-10 h-10 rounded-full bg-emerald-500 text-white flex items-center justify-center flex-shrink-0 shadow-md shadow-emerald-500/10">
<i class="uil uil-shield-check text-xl"></i>
</div>
<div class="text-sm leading-normal">
<span class="font-bold text-slate-800">Güvenli Belge Doğrulama:</span>
<span class="text-slate-600 ml-1">Bu staj sertifikası ve transkripti, Trunçgil Teknopark sistemi üzerinden dijital olarak imzalanıp onaylanmıştır.</span>
</div>
</div>
<div class="flex-shrink-0">
<!-- PDF Download Button -->
<button onclick="downloadPDF()" id="pdf-btn" class="px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white text-sm font-bold rounded-lg transition-all shadow-sm flex items-center gap-1.5 whitespace-nowrap">
<i class="uil uil-file-download text-base" id="pdf-icon"></i>
<span class="spinner-border spinner-border-sm hidden animate-spin w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full" id="pdf-spinner" role="status"></span>
</div>
</div>
</div>
</div>
<!-- Printable Area -->
<div class="print-container bg-slate-100 py-6 min-h-screen">
<div class="container max-w-6xl mx-auto px-4 pb-20 space-y-12">
<!-- ================= PAGE 1: CERTIFICATE (LANDSCAPE A4) ================= -->
<div class="certificate-container-wrapper overflow-hidden w-full flex justify-center items-start">
<div id="certificate-node" class="certificate-page bg-white rounded-[2rem] shadow-2xl p-12 relative overflow-hidden page-break mx-auto">
<!-- Watermark Logo -->
<div class="absolute inset-0 flex items-center justify-center pointer-events-none z-0 opacity-[0.05]">
<img src="{{ asset('logos/truncgil-dikey.svg') }}" class="w-[300px] h-[300px] object-contain">
</div>
<!-- Thin Minimal Orange/Slate Inner Border -->
<div class="relative z-10 flex flex-col justify-between h-full min-h-[580px] md:min-h-[540px]">
<!-- Logo & Header -->
<div class="relative flex justify-between items-center w-full min-h-[64px]">
<div class="w-1/3"></div>
<div class="w-1/3 flex justify-center">
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Teknoloji" class="h-16 md:h-20 object-contain">
</div>
<div class="w-1/3 text-right">
<span class="text-[10px] font-bold text-slate-400 tracking-widest block">VERİFİKASYON NO</span>
<span class="text-xs font-mono font-bold text-slate-800">{{ $application->certificate_code }}</span>
</div>
</div>
<!-- Main Content -->
<div class="text-center my-auto px-4">
<h1 class="font-cinzel text-3xl md:text-5xl text-slate-900 mt-8 mb-2" style="font-weight: 900; letter-spacing: 0.04em;">STAJ BİTİRME<br>SERTİFİKASI</h1>
<p class="text-slate-400 text-xs md:text-sm tracking-wider mb-0">SAYIN</p>
<h2 class="font-cinzel text-2xl md:text-4xl font-extrabold text-slate-900 tracking-wide mb-6 mt-0">{{ $application->name }}</h2>
<div class="max-w-3xl mx-auto text-slate-700 leading-relaxed text-sm md:text-base space-y-4">
<p>
<strong>{{ \Carbon\Carbon::parse($application->internship_start_date)->format('d.m.Y') }}</strong> -
<strong>{{ \Carbon\Carbon::parse($application->internship_end_date)->format('d.m.Y') }}</strong> tarihleri arasında,
<strong>Trunçgil Teknoloji</strong> bünyesinde gerçekleştirdiği staj programını başarıyla tamamlayarak bu belgeyi almaya hak kazanmıştır.
</p>
<p class="text-xs md:text-sm text-slate-500 max-w-2xl mx-auto italic font-serif">
Gösterdiği üstün gayret, sorumluluk bilinci, teknik beceriler ve ekip çalışmasına sağladığı değerli katkılardan dolayı teşekkür eder, profesyonel kariyerinde başarılar dileriz.
</p>
</div>
</div>
<!-- Footer Section (Signatures & Verification QR) -->
<div class="grid grid-cols-3 items-end pt-6 border-t border-slate-100/50">
<!-- Left: QR Code -->
<div class="flex items-center gap-4">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data={{ urlencode(route('internship.verify', $application->certificate_code)) }}" alt="QR Code" class="w-28 h-28 border border-slate-200 p-1 rounded-xl bg-white shadow-sm">
<div class="text-left hidden sm:block">
<span class="text-[8px] font-bold text-slate-400 tracking-wider block">DİJİTAL DOĞRULAMA</span>
<span class="text-[9px] text-slate-500 block">Karekod okutularak belgenin güncelliği doğrulanabilir.</span>
</div>
</div>
<!-- Middle: Date -->
<div class="text-center">
<span class="text-[9px] text-slate-600 block tracking-wider text-glow-white font-semibold">DÜZENLENME TARİHİ</span>
<span class="text-xs font-bold text-slate-800 text-glow-white">{{ \Carbon\Carbon::parse($application->internship_end_date)->format('d.m.Y') }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- ================= PAGE 2: TRANSCRIPT (PORTRAIT A4) ================= -->
<div class="transcript-container-wrapper overflow-hidden w-full flex justify-center items-start">
<div id="transcript-node" class="transcript-page bg-white rounded-[2rem] shadow-2xl p-6 md:p-8 relative overflow-hidden mx-auto">
<!-- Thin border decor for transcript -->
<div class="absolute inset-4 border border-slate-100 rounded-[1.5rem] pointer-events-none"></div>
<div class="relative z-10 flex flex-col justify-between h-full text-slate-700">
<div>
<!-- Header -->
<div class="flex justify-between items-center pb-2 border-b border-slate-100 mb-3">
<div>
<h2 class="font-serif font-bold text-slate-900 transcript-header-title">STAJ AKADEMİK TRANSKRİPTİ</h2>
<p class="text-slate-400 tracking-wider mt-0.5 transcript-header-subtitle">VE PERFORMANS DEĞERLENDİRME RAPORU</p>
</div>
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Logo" class="h-6 object-contain">
</div>
<!-- Student Info block -->
<div class="bg-slate-50 rounded-xl p-2.5 border border-slate-100 mb-3">
<h3 class="font-bold text-slate-400 tracking-widest mb-1.5 student-info-title">ÖĞRENCİ / STAJYER BİLGİLERİ</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-y-1 gap-x-8 student-info-grid">
<div class="flex justify-between border-b border-slate-200/50 pb-1">
<span class="text-slate-500">Adı Soyadı:</span>
<strong class="text-slate-800">{{ $application->name }}</strong>
</div>
<div class="flex justify-between border-b border-slate-200/50 pb-1">
<span class="text-slate-500">E-Posta Adresi:</span>
<strong class="text-slate-800">{{ $application->email }}</strong>
</div>
<div class="flex justify-between border-b border-slate-200/50 pb-1">
<span class="text-slate-500">Staj Dönemi:</span>
<strong class="text-slate-800">{{ \Carbon\Carbon::parse($application->internship_start_date)->format('d.m.Y') }} - {{ \Carbon\Carbon::parse($application->internship_end_date)->format('d.m.Y') }}</strong>
</div>
<div class="flex justify-between border-b border-slate-200/50 pb-1">
<span class="text-slate-500">Toplam Staj Süresi:</span>
<strong class="text-slate-800">{{ $application->internship_total_days }} İş Günü</strong>
</div>
</div>
</div>
<!-- Dynamic Markdown Content (Transcript and marks) -->
<div class="prose max-w-none text-slate-700 leading-relaxed mb-3">
{!! new \Illuminate\Support\HtmlString(\Illuminate\Support\Str::markdown($application->transcript_markdown ?? '')) !!}
</div>
</div>
<!-- Footer with verification details -->
<div class="w-full flex justify-between items-center pt-2 border-t border-slate-100 gap-4 mt-auto">
<div class="flex items-center gap-3">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data={{ urlencode(route('internship.verify', $application->certificate_code)) }}" alt="QR Code" class="w-14 h-14 border border-slate-200 p-0.5 rounded-lg bg-white">
<div>
<span class="font-bold text-slate-400 tracking-wider block verify-code-label">GÜVENLİ DOĞRULAMA KODU</span>
<span class="font-mono font-bold text-slate-800 block verify-code">{{ $application->certificate_code }}</span>
</div>
</div>
<div class="text-right text-slate-400 footer-address">
Trunçgil Teknoloji San. ve Tic. Ltd. Şti.<br>
Gaziantep Üniversitesi Teknopark No: 4A
</div>
</div>
</div>
</div>
</div>
<!-- ================= PAGE 3: STAJ DEFTERİ (PORTRAIT A4) ================= -->
@if($application->notebook_approved || $application->notebook_supervisor_signed)
<div class="notebook-container-wrapper overflow-hidden w-full flex justify-center items-start mt-8">
<div id="notebook-node" class="transcript-page bg-white rounded-[2rem] shadow-2xl p-6 md:p-8 relative overflow-hidden mx-auto">
<!-- Thin border decor for notebook -->
<div class="absolute inset-4 border border-slate-100 rounded-[1.5rem] pointer-events-none"></div>
<div class="relative z-10 flex flex-col justify-between h-full text-slate-700">
<div>
<!-- Header -->
<div class="flex justify-between items-center pb-2 border-b border-slate-100 mb-3">
<div>
<h2 class="font-serif font-bold text-slate-900 transcript-header-title">ONAYLI STAJ DEFTERİ RAPORU</h2>
<p class="text-slate-400 tracking-wider mt-0.5 transcript-header-subtitle">DİJİTAL İMZALI VE ONAYLI STAJ RAPORLARI</p>
</div>
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Logo" class="h-6 object-contain">
</div>
<!-- Signatures Information -->
<div class="bg-emerald-50 border border-emerald-200 rounded-xl p-3 mb-4 flex justify-between items-center">
<div>
<span class="text-xs font-extrabold text-emerald-800 uppercase block tracking-wider">Dijital İmza Durumu</span>
<span class="text-[10px] text-emerald-600 font-semibold block mt-0.5">Bu staj defteri yetkililer tarafından elektronik olarak imzalanmıştır.</span>
</div>
<div class="flex gap-2">
@if($application->notebook_supervisor_signed)
<span class="inline-flex px-2 py-1 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">
Sorumlu: {{ $application->notebook_supervisor_name ?: 'Alperen Trunç' }}
</span>
@endif
</div>
</div>
<!-- Notebook Days content overview -->
<div class="overflow-y-auto max-h-[700px] pr-2 no-scrollbar space-y-4">
@foreach($days as $day)
@php
$entry = $savedEntries->get($day['day_number']);
@endphp
@if($entry && trim($entry->content))
<div class="p-3 bg-slate-50 border border-slate-100 rounded-xl">
<div class="flex justify-between items-center pb-1.5 border-b border-slate-200/50 mb-1.5">
<span class="text-xs font-extrabold text-slate-800">
{{ $day['day_number'] }}. Gün Raporu
@if($entry->supervisor_approved)
<span class="ml-2 inline-flex px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-800 text-[8px] font-extrabold uppercase border border-emerald-200">Sorumlu Onaylı{{ $entry->supervisor_name ? ' (' . $entry->supervisor_name . ')' : '' }}</span>
@endif
</span>
<span class="text-[10px] text-slate-400 font-bold">{{ $day['formatted_date'] }}</span>
</div>
<div class="text-xs text-slate-600 leading-relaxed verify-log-content" style="font-size: 11px;">{!! $entry->content !!}</div>
</div>
@endif
@endforeach
</div>
</div>
<!-- Footer -->
<div class="w-full flex justify-between items-center pt-2 border-t border-slate-100 gap-4 mt-auto">
<div class="flex items-center gap-3">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data={{ urlencode(route('internship.verify', $application->certificate_code)) }}" alt="QR Code" class="w-14 h-14 border border-slate-200 p-0.5 rounded-lg bg-white">
<div>
<span class="font-bold text-slate-400 tracking-wider block verify-code-label">GÜVENLİ DOĞRULAMA KODU</span>
<span class="font-mono font-bold text-slate-800 block verify-code">{{ $application->certificate_code }}</span>
</div>
</div>
<div class="text-right text-slate-400 footer-address">
Trunçgil Teknoloji San. ve Tic. Ltd. Şti.<br>
Gaziantep Üniversitesi Teknopark No: 4A
</div>
</div>
</div>
</div>
</div>
@endif
</div>
</div>
@else
<div class="min-h-screen bg-slate-100 flex items-center justify-center p-6 no-print">
<div class="bg-white rounded-3xl p-8 max-w-md w-full shadow-2xl border border-slate-100 text-center">
<div class="w-16 h-16 rounded-full bg-red-50 text-[#e31e24] flex items-center justify-center text-3xl mx-auto mb-4">
<i class="uil uil-exclamation-triangle"></i>
</div>
<h1 class="text-xl font-bold text-slate-800">Doğrulanamayan Belge</h1>
<p class="text-xs text-slate-500 mt-2 leading-relaxed">Görüntülemeye çalıştığınız staj kaydı henüz onaylanmamıştır veya aktif değildir. Detaylı bilgi için kurum yetkilileri ile iletişime geçebilirsiniz.</p>
<a href="/" class="mt-6 inline-flex px-5 py-2.5 bg-slate-800 hover:bg-slate-900 text-white font-bold rounded-xl text-xs transition-all shadow-md">Anasayfaya Dön</a>
</div>
</div>
@endif
@push('styles')
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<style>
:root {
/* Global scale factor for transcript page fonts. Adjust this easily (e.g. 1.0, 1.10, 1.20) */
--transcript-font-scale: 1.30;
}
.verify-log-content ul {
list-style-type: disc !important;
padding-left: 18px !important;
margin-top: 5px !important;
margin-bottom: 5px !important;
}
.verify-log-content ol {
list-style-type: decimal !important;
padding-left: 18px !important;
margin-top: 5px !important;
margin-bottom: 5px !important;
}
.verify-log-content li {
margin-bottom: 3px !important;
}
body {
font-family: 'Inter', sans-serif;
}
.font-serif {
font-family: 'Cinzel', 'Playfair Display', Georgia, serif;
}
.font-cinzel {
font-family: 'Cinzel', serif !important;
}
.text-glow-white {
text-shadow: 0 0 10px #ffffff, 0 0 6px #ffffff, 0 0 3px #ffffff;
}
/* Transcript typography helpers scaled globally */
.transcript-header-title {
font-size: calc(0.875rem * var(--transcript-font-scale)) !important;
}
.transcript-header-subtitle {
font-size: calc(7px * var(--transcript-font-scale)) !important;
}
.student-info-title {
font-size: calc(7.5px * var(--transcript-font-scale)) !important;
}
.student-info-grid {
font-size: calc(8.5px * var(--transcript-font-scale)) !important;
}
.verify-code-label {
font-size: calc(6.5px * var(--transcript-font-scale)) !important;
}
.verify-code {
font-size: calc(9px * var(--transcript-font-scale)) !important;
}
.footer-address {
font-size: calc(7.5px * var(--transcript-font-scale)) !important;
}
/* Screen display aspect styling */
.certificate-page {
width: 1122px;
min-height: 794px;
box-sizing: border-box;
background-image: url('{{ asset("assets/img/cert_bg_futuristic.png") }}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-color: #ffffff;
}
.transcript-page {
width: 794px;
height: 1122px;
box-sizing: border-box;
}
/* Elegant Markdown Table Styling inside prose (Reduced font sizes & paddings) */
.prose table {
width: 100%;
margin-top: 0.35rem !important;
margin-bottom: 0.35rem !important;
border-collapse: collapse;
text-align: left;
}
.prose th {
font-weight: 700;
padding: 0.2rem 0.35rem !important;
background-color: #f8fafc;
border-bottom: 2px solid #e2e8f0;
color: #1e293b;
font-size: calc(0.45rem * var(--transcript-font-scale)) !important;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.prose td {
padding: 0.2rem 0.35rem !important;
border-bottom: 1px solid #e2e8f0;
color: #334155;
font-size: calc(0.45rem * var(--transcript-font-scale)) !important;
line-height: 1.2 !important;
}
.prose tr:hover {
background-color: #f8fafc;
}
.prose h3 {
font-family: 'Cinzel', serif;
color: #0f172a;
margin-top: 0.5rem !important;
margin-bottom: 0.25rem !important;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 0.15rem;
font-weight: 700;
font-size: calc(0.6rem * var(--transcript-font-scale)) !important;
}
.prose h4 {
color: #1e293b;
margin-top: 0.4rem !important;
margin-bottom: 0.2rem !important;
font-weight: 600;
font-size: calc(0.52rem * var(--transcript-font-scale)) !important;
}
.prose p {
font-size: calc(0.45rem * var(--transcript-font-scale)) !important;
line-height: 1.3 !important;
margin-top: 0.15rem !important;
margin-bottom: 0.2rem !important;
}
.prose ul {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.1rem 0.75rem;
padding-left: 0 !important;
margin-top: 0.25rem !important;
margin-bottom: 0.25rem !important;
list-style-type: none !important;
}
.prose ul li {
font-size: calc(0.45rem * var(--transcript-font-scale)) !important;
line-height: 1.3 !important;
margin: 0 !important;
position: relative;
padding-left: 0.5rem !important;
}
.prose ul li::before {
content: "•";
color: #f97316;
position: absolute;
left: 0;
top: 0;
font-weight: bold;
}
.prose blockquote {
border-left: 2px solid #f97316;
padding-left: 0.4rem !important;
font-style: italic;
color: #475569;
margin: 0.4rem 0 !important;
font-size: calc(0.45rem * var(--transcript-font-scale)) !important;
}
/* Print media config for mixed Landscape & Portrait */
@media print {
@page {
margin: 0;
}
@page landscape-page {
size: A4 landscape;
}
@page portrait-page {
size: A4 portrait;
}
body, html {
background-color: #ffffff !important;
margin: 0 !important;
padding: 0 !important;
}
.no-print, header, footer, nav, .site-header, .site-footer {
display: none !important;
}
.print-container {
position: absolute;
left: 0;
top: 0;
width: 100% !important;
padding: 0 !important;
margin: 0 !important;
background-color: transparent !important;
}
.certificate-container-wrapper, .transcript-container-wrapper {
height: auto !important;
overflow: visible !important;
}
.certificate-page {
page: landscape-page;
transform: none !important;
width: 297mm !important;
height: 210mm !important;
min-height: 210mm !important;
margin: 0 !important;
padding: 15mm !important;
box-shadow: none !important;
border: none !important;
border-radius: 0 !important;
box-sizing: border-box !important;
page-break-after: always !important;
break-after: page !important;
background-image: url('{{ asset("assets/img/cert_bg_futuristic.png") }}') !important;
background-size: cover !important;
background-position: center !important;
background-repeat: no-repeat !important;
background-color: #ffffff !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.transcript-page {
page: portrait-page;
transform: none !important;
width: 210mm !important;
height: 297mm !important;
min-height: 297mm !important;
margin: 0 !important;
padding: 10mm !important;
box-shadow: none !important;
border: none !important;
border-radius: 0 !important;
box-sizing: border-box !important;
overflow: hidden !important;
}
}
</style>
@endpush
@push('scripts')
<script>
function scaleDocument(wrapperSelector, nodeSelector, baseWidth, baseHeight) {
const wrapper = document.querySelector(wrapperSelector);
const node = document.querySelector(nodeSelector);
if (!wrapper || !node) return;
const containerWidth = wrapper.offsetWidth;
if (containerWidth < baseWidth) {
const scale = containerWidth / baseWidth;
node.style.transform = `scale(${scale})`;
node.style.transformOrigin = 'top center';
node.style.width = `${baseWidth}px`;
node.style.minWidth = `${baseWidth}px`;
node.style.height = `${baseHeight}px`;
node.style.minHeight = `${baseHeight}px`;
wrapper.style.height = `${baseHeight * scale}px`;
} else {
node.style.transform = '';
node.style.transformOrigin = '';
node.style.width = '';
node.style.minWidth = '';
node.style.height = '';
node.style.minHeight = '';
wrapper.style.height = '';
}
}
function scaleAllDocuments() {
scaleDocument('.certificate-container-wrapper', '#certificate-node', 1122, 794);
scaleDocument('.transcript-container-wrapper', '#transcript-node', 794, 1122);
scaleDocument('.notebook-container-wrapper', '#notebook-node', 794, 1122);
}
window.addEventListener('resize', scaleAllDocuments);
window.addEventListener('DOMContentLoaded', scaleAllDocuments);
window.addEventListener('load', scaleAllDocuments);
// Immediate and delayed triggers to ensure correct measurement after image & stylesheet loading
scaleAllDocuments();
setTimeout(scaleAllDocuments, 100);
setTimeout(scaleAllDocuments, 500);
setTimeout(scaleAllDocuments, 1500);
async function downloadPDF() {
const btn = document.getElementById('pdf-btn');
const icon = document.getElementById('pdf-icon');
const spinner = document.getElementById('pdf-spinner');
const btnText = document.getElementById('pdf-btn-text');
// Show spinner
btn.disabled = true;
icon.classList.add('hidden');
spinner.classList.remove('hidden');
btnText.textContent = "PDF Hazırlanıyor...";
try {
const { jsPDF } = window.jspdf;
const certEl = document.getElementById('certificate-node');
const transEl = document.getElementById('transcript-node');
const notebookEl = document.getElementById('notebook-node');
// Force elements to normal scale prior to capturing for PDF
const origCertTransform = certEl.style.transform;
const origCertWidth = certEl.style.width;
const origCertHeight = certEl.style.height;
const origCertRadius = certEl.style.borderRadius;
const origTransTransform = transEl.style.transform;
const origTransWidth = transEl.style.width;
const origTransHeight = transEl.style.height;
const origTransRadius = transEl.style.borderRadius;
let origNotebookTransform, origNotebookWidth, origNotebookHeight, origNotebookRadius;
if (notebookEl) {
origNotebookTransform = notebookEl.style.transform;
origNotebookWidth = notebookEl.style.width;
origNotebookHeight = notebookEl.style.height;
origNotebookRadius = notebookEl.style.borderRadius;
notebookEl.style.transform = 'none';
notebookEl.style.width = '794px';
notebookEl.style.height = '1122px';
notebookEl.style.borderRadius = '0';
}
certEl.style.transform = 'none';
certEl.style.width = '1122px';
certEl.style.height = '794px';
certEl.style.borderRadius = '0';
transEl.style.transform = 'none';
transEl.style.width = '794px';
transEl.style.height = '1122px';
transEl.style.borderRadius = '0';
// Render Page 1 (Landscape)
const certCanvas = await html2canvas(certEl, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
logging: false
});
const certImgData = certCanvas.toDataURL('image/jpeg', 1.0);
// Initialize landscape A4 document
const pdf = new jsPDF({
orientation: 'landscape',
unit: 'mm',
format: 'a4'
});
pdf.addImage(certImgData, 'JPEG', 0, 0, 297, 210);
// Render Page 2 (Portrait)
const transCanvas = await html2canvas(transEl, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
logging: false
});
const transImgData = transCanvas.toDataURL('image/jpeg', 1.0);
// Calculate exact height using aspect ratio to prevent vertical squishing
const transWidth = 210;
const transHeight = (transCanvas.height * transWidth) / transCanvas.width;
// Add portrait page to PDF
pdf.addPage('a4', 'portrait');
pdf.addImage(transImgData, 'JPEG', 0, 0, transWidth, transHeight);
// Render Page 3 (Notebook) if it exists
if (notebookEl) {
const notebookCanvas = await html2canvas(notebookEl, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
logging: false
});
const notebookImgData = notebookCanvas.toDataURL('image/jpeg', 1.0);
const notebookHeight = (notebookCanvas.height * transWidth) / notebookCanvas.width;
pdf.addPage('a4', 'portrait');
pdf.addImage(notebookImgData, 'JPEG', 0, 0, transWidth, notebookHeight);
}
// Restore styles
certEl.style.transform = origCertTransform;
certEl.style.width = origCertWidth;
certEl.style.height = origCertHeight;
certEl.style.borderRadius = origCertRadius;
transEl.style.transform = origTransTransform;
transEl.style.width = origTransWidth;
transEl.style.height = origTransHeight;
transEl.style.borderRadius = origTransRadius;
if (notebookEl) {
notebookEl.style.transform = origNotebookTransform;
notebookEl.style.width = origNotebookWidth;
notebookEl.style.height = origNotebookHeight;
notebookEl.style.borderRadius = origNotebookRadius;
}
// Trigger Save File
pdf.save('{{ str()->slug($application->name) }}-staj-belgesi.pdf');
} catch (error) {
console.error("PDF generation failed", error);
alert("PDF oluşturulurken bir hata meydana geldi.");
} finally {
// Restore button
btn.disabled = false;
icon.classList.remove('hidden');
spinner.classList.add('hidden');
btnText.textContent = "PDF Olarak İndir";
}
}
</script>
@endpush
@endsection
@@ -103,39 +103,50 @@
@foreach($productions as $index => $production)
@php
$variant = $variants[$index % count($variants)];
$categoryLabel = $production->translate('client_name') ?: __('music_productions.card_category_default');
$detailUrl = route('music-productions.show', $production->slug);
$fullTitle = $production->translate('title');
$titlePart = $fullTitle;
$stylePart = '';
if (preg_match('/^(.*?)\s*\((.*?)\)\s*$/u', $fullTitle, $matches)) {
$titlePart = trim($matches[1]);
$stylePart = trim($matches[2]);
}
if (empty($stylePart)) {
$stylePart = $production->translate('client_name') ?: __('music_productions.card_category_default');
}
@endphp
<div class="project item xl:w-4/12 lg:w-6/12 md:w-6/12 w-full flex-[0_0_auto] max-w-full {{ $variant['filter'] }} px-[20px] xl:!px-[25px] lg:!px-[25px] !mt-[2rem] xl:!mt-[2.5rem] lg:!mt-[2.5rem]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]">
<figure class="card-img-top music-production-cover itooltip {{ $variant['tooltip'] }}" title='<h5 class="!mb-0">{{ e(__('music_productions.view_details_tooltip')) }}</h5>'>
<a href="{{ $detailUrl }}" class="music-production-cover__link">
@if($production->cover_image_url)
<img src="{{ $production->cover_image_url }}" alt="{{ $production->translate('title') }}" loading="lazy">
@else
<img src="{{ asset('assets/img/photos/pd6.jpg') }}" alt="{{ $production->translate('title') }}" loading="lazy">
@endif
</a>
</figure>
<div class="card-body p-7">
<div class="post-header">
<div class="d-none {{ $variant['label_class'] }}">
<div class="player-container">
<div class="player" data-player data-playlist="prod-{{ $production->id }}" data-track="0">
<div class="player__content">
<div class="player__artist" title="{{ $titlePart }}">{{ $titlePart }}</div>
<div class="player__album" title="{{ $stylePart }}">{{ $stylePart }}</div>
<div class="player__track">
@if($production->production_date)
{{ $production->production_date->format('d.m.Y') }}
@else
{{ __('music_productions.production_date_unknown') }}
&nbsp;
@endif
</div>
<h3 class="!mb-0 truncate" title="{{ $production->translate('title') }}">{{ $production->translate('title') }}</h3>
</div>
<!-- /.post-header -->
<a href="{{ $detailUrl }}" class="album" data-album>
@if($production->cover_image_url)
<div class="album__cover" style="background-image: url('{{ $production->cover_image_url }}');"></div>
@else
<div class="album__cover" style="background-image: url('{{ asset('assets/img/photos/pd6.jpg') }}');"></div>
@endif
<div class="vinyl">
<div class="vinyl__shadow"></div>
<div class="vinyl__circle">
<div class="vinyl__center" style="--center-bg: url('{{ $production->cover_image_url ?: asset('assets/img/photos/pd6.jpg') }}')"></div>
</div>
</div>
</a>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.project -->
@endforeach
</div>
<!-- /.row -->
@@ -308,5 +319,251 @@
opacity: 0.5;
cursor: not-allowed;
}
/* --- Vinyl Player Styles --- */
:root {
--color-black: #000;
--color-gray: #5e6062;
--color-white: #fff;
--color-yellow: #f0f39c;
--color-yellow-light: #fff99e;
--transition-time: 550ms;
--transition-method: cubic-bezier(0.34, 1.56, 0.64, 1);
--album-size: 260px;
--vinyl-offset: 15px;
--vinyl-size: calc(var(--album-size) - var(--vinyl-offset));
--vinyl-center-size: 110px;
}
.player-container {
display: flex;
justify-content: center;
align-items: center;
padding: 15px 0;
perspective: 1000px;
}
.player {
color: var(--color-gray);
font-family: 'Raleway', 'Outfit', sans-serif;
height: var(--album-size);
line-height: 1.2;
position: relative;
width: var(--album-size);
transition: all var(--transition-time) var(--transition-method);
margin: 0 auto;
}
.player::after {
transform: rotate(-2deg);
transition: all var(--transition-time) var(--transition-method);
background: #777;
bottom: 12px;
box-shadow: 0 15px 23px rgba(0, 0, 0, 0.3);
content: '';
height: 10px;
left: 2%;
opacity: 0;
padding-left: 5%;
position: absolute;
width: 96%;
z-index: 5;
}
.player:hover::after {
opacity: 0.6;
}
.player .player__content {
background-color: var(--color-white);
height: 100%;
padding: 50px 15px 0 15px;
position: absolute;
left: 0;
top: 0;
text-align: left;
width: 100%;
z-index: 50;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
display: flex;
flex-direction: column;
justify-content: flex-start;
border: 1px solid rgba(8, 60, 130, 0.05);
transition: all var(--transition-time) var(--transition-method);
}
.player .player__artist {
font-size: 16px;
font-weight: 600;
margin-bottom: 3px;
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #343f52;
}
.player .player__album {
font-size: 13px;
font-weight: 300;
margin-bottom: 15px;
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #a4aec6;
}
.player .player__track {
font-size: 12px;
font-weight: 400;
margin-bottom: 15px;
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #60697b;
}
.player .player__track::before,
.player .player__track::after {
content: none;
}
.album {
transition: all var(--transition-time) var(--transition-method);
box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.15);
cursor: pointer;
height: 100%;
position: absolute;
left: 0;
top: 0;
width: 100%;
z-index: 100;
border-radius: 8px;
}
.project.item:has(.player:hover) {
z-index: 9999 !important;
}
.player:hover {
z-index: 999;
}
.player:hover .player__content {
transform: translateX(-160px);
box-shadow: -10px 15px 35px rgba(0,0,0,0.1);
}
.player:hover .album {
transform: scale(1.2);
z-index: 150;
box-shadow: 0px 15px 35px rgba(0, 0, 0, 0.25);
}
.album__cover {
background-color: var(--color-black);
background-size: cover;
background-position: center;
height: 100%;
position: relative;
width: 100%;
z-index: 20;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.vinyl {
transition: all var(--transition-time) var(--transition-method);
left: calc(var(--vinyl-offset) / 2);
position: absolute;
top: calc(var(--vinyl-offset) / 2);
height: var(--vinyl-size);
width: var(--vinyl-size);
z-index: 10;
}
.vinyl__shadow,
.vinyl__circle {
border-radius: 100%;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.vinyl__shadow {
box-shadow: 2px 8px 15px rgba(0, 0, 0, 0.2);
z-index: 5;
}
.vinyl__circle {
animation: rotate-vinyl 3s linear infinite both paused;
background-color: #1e1b1b;
background-image: radial-gradient(circle, #2c2727 10%, transparent 10%),
repeating-radial-gradient(circle, #2c2727, #1e1b1b 2px, #2c2727 4px);
z-index: 10;
transition: box-shadow var(--transition-time) var(--transition-method);
}
.player:hover .vinyl {
left: 60%;
}
.player:hover .vinyl__circle {
animation-play-state: running;
box-shadow: 0 0 25px rgba(0, 0, 0, 0.35), 0 0 50px rgba(3, 3, 3, 0.35);
}
.vinyl__center {
transform: translate(-50%, -50%);
background-color: #292424;
border-radius: 100%;
height: var(--vinyl-center-size);
left: 50%;
position: absolute;
top: 50%;
width: var(--vinyl-center-size);
}
.vinyl__center::before,
.vinyl__center::after {
transform: translate(-50%, -50%);
border-radius: 100%;
content: '';
left: 50%;
position: absolute;
top: 50%;
}
.vinyl__center::before {
background-color: #e31e24;
background-image: var(--center-bg);
background-size: cover;
background-position: center;
height: calc(var(--vinyl-center-size) - 20px);
width: calc(var(--vinyl-center-size) - 20px);
z-index: 5;
}
.vinyl__center::after {
background-color: #fff;
height: 20px;
width: 20px;
z-index: 10;
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
}
@keyframes rotate-vinyl {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
@endpush
@@ -1,6 +1,80 @@
@extends('layouts.site')
@section('content')
@php
$fullTitle = $production->translate('title');
$titlePart = $fullTitle;
$stylePart = '';
if (preg_match('/^(.*?)\s*\((.*?)\)\s*$/u', $fullTitle, $matches)) {
$titlePart = trim($matches[1]);
$stylePart = trim($matches[2]);
}
$contentHtml = $production->translate('content');
$isYoutubeFormat = false;
if (stripos($contentHtml, 'Provided to YouTube') !== false || stripos($contentHtml, 'Auto-generated by YouTube') !== false) {
$isYoutubeFormat = true;
}
$trackNameParsed = null;
$styleParsed = null;
$distributor = null;
$contributors = [];
$label = null;
$releasedOn = null;
$remainingLines = [];
if ($isYoutubeFormat) {
$rawText = strip_tags(str_replace(['<p>', '</p>', '<br>', '<br />', '<br/>'], ["\n", "\n", "\n", "\n", "\n"], $contentHtml));
$lines = array_filter(array_map('trim', explode("\n", $rawText)));
foreach ($lines as $line) {
if (empty($line)) continue;
if (preg_match('/Provided to YouTube by\s+(.+)/i', $line, $m)) {
$distributor = trim($m[1]);
}
elseif (strpos($line, '·') !== false) {
$parts = array_map('trim', explode('·', $line));
if (count($parts) > 0) {
$trackPart = $parts[0];
if (preg_match('/^(.*?)\s*\((.*?)\)\s*$/u', $trackPart, $tm)) {
$trackNameParsed = trim($tm[1]);
$styleParsed = trim($tm[2]);
} else {
$trackNameParsed = $trackPart;
}
for ($i = 1; $i < count($parts); $i++) {
$contributors[] = $parts[$i];
}
}
}
elseif (preg_match('/℗\s*(.+)/u', $line, $m)) {
$label = trim($m[1]);
}
elseif (preg_match('/Released on:\s*(.+)/i', $line, $m)) {
$releasedOn = trim($m[1]);
}
elseif (stripos($line, 'Auto-generated by YouTube') !== false) {
// Skip line
}
elseif (stripos($line, 'YouTube\'da İzle') !== false || stripos($line, 'Watch on YouTube') !== false) {
// Skip line
}
else {
$normLine = trim(strtolower($line));
$normTitle = trim(strtolower($production->translate('title')));
if ($normLine === $normTitle || ($trackNameParsed && $normLine === trim(strtolower($trackNameParsed)))) {
continue;
}
$remainingLines[] = $line;
}
}
}
@endphp
<section class="wrapper !bg-[#edf2fc]">
<div class="container pt-10 pb-36 xl:pt-[4.5rem] lg:pt-[4.5rem] md:pt-[4.5rem] xl:pb-60 lg:pb-60 md:pb-60 !text-center">
<div class="flex flex-wrap mx-[-15px]">
@@ -11,7 +85,10 @@
<span>{{ $production->translate('client_name') }}</span>
</div>
@endif
<h1 class="!text-[calc(1.365rem_+_1.38vw)] font-bold !leading-[1.2] xl:!text-[2.4rem] !mb-3">{{ $production->translate('title') }}</h1>
<h1 class="!text-[calc(1.365rem_+_1.38vw)] font-bold !leading-[1.2] xl:!text-[2.4rem] !mb-3">{{ $titlePart }}</h1>
@if($stylePart || $styleParsed)
<p class="lead !text-[1.15rem] !mb-4 font-semibold text-[#747ed1]">{{ $stylePart ?: $styleParsed }}</p>
@endif
@if($production->production_date)
<p class="lead !leading-[1.65] text-[.9rem] font-medium text-[#60697b]">
<i class="uil uil-calendar-alt before:content-['\e9ba'] !mr-1"></i>
@@ -29,11 +106,70 @@
<div class="flex flex-wrap mx-[-15px]">
<div class="w-full flex-[0_0_auto] !px-[15px] max-w-full">
<article class="!mt-[-12.5rem]">
{{-- Cover Image --}}
@php
$videoId = null;
if ($production->youtube_video_id) {
$videoId = $production->youtube_video_id;
} elseif ($production->youtube_url) {
if (preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $production->youtube_url, $match)) {
$videoId = $match[1];
}
}
@endphp
{{-- Vinyl Player Mockup --}}
@if($production->cover_image_url)
<figure class="!rounded-[.4rem] !mb-8 xl:!mb-[3.5rem] lg:!mb-[3.5rem] md:!mb-[3.5rem] overflow-hidden shadow-lg">
<img class="!rounded-[.4rem] w-full" src="{{ $production->cover_image_url }}" alt="{{ $production->translate('title') }}">
</figure>
<div class="player-container flex justify-center !mb-12 xl:!mb-16">
<div class="player player--open" id="detail-vinyl-player" data-player>
<div class="player__content">
<div class="player__artist has-fade-in" title="{{ $titlePart }}">{{ $titlePart }}</div>
<div class="player__album has-fade-in" title="{{ $stylePart ?: ($production->translate('client_name') ?: __('music_productions.card_category_default')) }}">{{ $stylePart ?: ($production->translate('client_name') ?: __('music_productions.card_category_default')) }}</div>
<div class="player__track has-fade-in">
@if($production->production_date)
{{ $production->production_date->format('d.m.Y') }}
@else
&nbsp;
@endif
</div>
<div class="album" data-album>
<div class="album__cover" style="background-image: url('{{ $production->cover_image_url }}');"></div>
<div class="vinyl">
<div class="vinyl__shadow"></div>
<div class="vinyl__circle">
<div class="vinyl__center" style="--center-bg: url('{{ $production->cover_image_url }}')"></div>
</div>
</div>
</div>
<div class="player__controls has-fade-in">
@if($prev)
<a href="{{ route('music-productions.show', $prev->slug) }}" class="player__controls-item player__controls-item--prev" title="{{ __('music_productions.prev') }}"></a>
@else
<div class="player__controls-item player__controls-item--prev opacity-30 cursor-not-allowed"></div>
@endif
<div class="player__controls-item player__controls-item--play" id="play-cd-btn" title="{{ __('music_productions.youtube_watch_on_youtube') }}">
<div class="rectangle"></div>
<div class="triangle"></div>
</div>
@if($next)
<a href="{{ route('music-productions.show', $next->slug) }}" class="player__controls-item player__controls-item--next" title="{{ __('music_productions.next') }}"></a>
@else
<div class="player__controls-item player__controls-item--next opacity-30 cursor-not-allowed"></div>
@endif
</div>
</div>
</div>
</div>
@endif
{{-- Hidden Background YouTube Player --}}
@if($videoId)
<div id="hidden-youtube-player-container" style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; overflow: hidden; left: -9999px;">
<div id="hidden-youtube-player"></div>
</div>
@endif
{{-- Content & Meta --}}
@@ -41,44 +177,69 @@
<div class="xl:w-10/12 xl:!ml-[8.33333333%] lg:w-10/12 lg:!ml-[8.33333333%] w-full flex-[0_0_auto] !px-[15px] max-w-full">
<h2 class="!text-[calc(1.265rem_+_0.18vw)] font-bold xl:!text-[1.4rem] !leading-[1.35] !mb-4">{{ __('music_productions.project_details') }}</h2>
<div class="flex flex-wrap mx-0">
{{-- Content column --}}
<div class="xl:w-9/12 lg:w-9/12 md:w-9/12 w-full flex-[0_0_auto] max-w-full">
<div class="music-production-content prose max-w-none">
{!! $production->translate('content') !!}
</div>
</div>
{{-- Sidebar - Date & Client --}}
<div class="xl:w-2/12 lg:w-2/12 md:w-2/12 w-full flex-[0_0_auto] max-w-full !ml-auto">
<ul class="pl-0 list-none">
@if($production->production_date)
<li>
<h5 class="!mb-1">{!! t('Tarih') !!}</h5>
<p>{{ $production->production_date->format('d M Y') }}</p>
</li>
@endif
@if($production->client_name)
<li>
<h5 class="!mb-1">{!! t('Müşteri') !!}</h5>
<p>{{ $production->translate('client_name') }}</p>
</li>
@endif
@if($production->spotify_url)
<li>
<h5 class="!mb-1">Spotify</h5>
<p>
<a href="{{ $production->spotify_url }}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-soft-primary !rounded-[50rem]">
{{ __('music_productions.spotify_listen_on_spotify') }}
</a>
</p>
</li>
@endif
</ul>
<a href="{{ route('music-productions.index') }}" class="more hover">
<i class="uil uil-arrow-left before:content-['\e949'] !mr-1"></i>
{{ __('music_productions.back_to_list') }}
</a>
</div>
{{-- Premium Metadata Table --}}
<div class="prod-meta-card">
<table class="prod-meta-table">
<tbody>
<tr>
<td class="prod-meta-label"><i class="uil uil-music"></i> {{ __("music_productions.track") }}</td>
<td class="prod-meta-value">{{ $trackNameParsed ?? $titlePart }}</td>
</tr>
@if($styleParsed)
<tr>
<td class="prod-meta-label"><i class="uil uil-layer-group"></i> {{ __("music_productions.style") }}</td>
<td class="prod-meta-value"><span class="prod-meta-badge">{{ $styleParsed }}</span></td>
</tr>
@endif
@if(!empty($contributors))
<tr>
<td class="prod-meta-label"><i class="uil uil-users-alt"></i> {{ __("music_productions.contributors") }}</td>
<td class="prod-meta-value">{{ implode(', ', $contributors) }}</td>
</tr>
@endif
@if($releasedOn)
<tr>
<td class="prod-meta-label"><i class="uil uil-calendar-alt"></i> {{ __("music_productions.released_on") }}</td>
<td class="prod-meta-value">{{ \Carbon\Carbon::parse($releasedOn)->format('d.m.Y') }}</td>
</tr>
@endif
@if($label)
<tr>
<td class="prod-meta-label"><i class="uil uil-record-audio"></i> {{ __("music_productions.label") }}</td>
<td class="prod-meta-value">{{ $label }}</td>
</tr>
@endif
@if($production->spotify_url)
<tr>
<td class="prod-meta-label"><i class="uil uil-headphones"></i> Spotify</td>
<td class="prod-meta-value">
<a href="{{ $production->spotify_url }}" target="_blank" rel="noopener noreferrer" class="prod-platform-btn prod-platform-btn--spotify">
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/></svg>
{{ __('music_productions.spotify_listen_on_spotify') }}
</a>
</td>
</tr>
@endif
@if($production->youtube_url)
<tr>
<td class="prod-meta-label"><i class="uil uil-youtube"></i> {{ __("music_productions.youtube") }}</td>
<td class="prod-meta-value">
<a href="{{ $production->youtube_url }}" target="_blank" rel="noopener noreferrer" class="prod-platform-btn prod-platform-btn--youtube">
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
{{ __('music_productions.youtube_watch_on_youtube') }}
</a>
</td>
</tr>
@endif
</tbody>
</table>
</div>
</div>
</div>
</div>
@@ -134,6 +295,415 @@
@push('styles')
<style>
/* --- Vinyl Player Styles --- */
:root {
--color-black: #000;
--color-gray: #5e6062;
--color-white: #fff;
--color-yellow: #f0f39c;
--color-yellow-light: #fff99e;
--transition-time: 550ms;
--transition-method: cubic-bezier(0.34, 1.56, 0.64, 1);
--album-size: 310px;
--vinyl-offset: 20px;
--vinyl-size: calc(var(--album-size) - var(--vinyl-offset));
--vinyl-center-size: 130px;
}
.player-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px 0;
perspective: 1000px;
}
.player {
color: var(--color-gray);
font-family: 'Raleway', 'Outfit', sans-serif;
height: var(--album-size);
line-height: 1.2;
position: relative;
width: var(--album-size);
transition: all var(--transition-time) var(--transition-method);
margin: 0 auto;
}
.player::after {
transform: rotate(-2deg);
transition: all var(--transition-time) var(--transition-method);
background: #777;
bottom: 12px;
box-shadow: 0 15px 23px rgba(0, 0, 0, 0.3);
content: '';
height: 10px;
left: 2%;
opacity: 0;
padding-left: 5%;
position: absolute;
width: 96%;
z-index: 5;
}
.player--open::after {
opacity: 0.6;
}
.player .player__content {
background-color: var(--color-white);
height: 135%;
padding: 70px 20px 0 20px;
position: relative;
text-align: left;
width: 100%;
z-index: 10;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
display: flex;
flex-direction: column;
justify-content: flex-start;
border: 1px solid rgba(8, 60, 130, 0.05);
}
.player .player__artist {
font-size: 18px;
font-weight: 600;
margin-bottom: 5px;
max-width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #343f52;
}
.player .player__album {
font-size: 15px;
font-weight: 300;
margin-bottom: 20px;
max-width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #a4aec6;
}
.player .player__track {
font-size: 14px;
font-weight: 400;
margin-bottom: 20px;
max-width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #60697b;
}
.player .player__track::before,
.player .player__track::after {
content: '"';
font-size: 16px;
vertical-align: middle;
}
.player--open {
border-radius: 8px;
height: 320px;
position: relative;
width: 440px;
}
.player__controls {
position: absolute;
bottom: 25px;
left: 20px;
text-align: left;
white-space: nowrap;
width: 180px;
z-index: 15;
}
.player__controls-item {
user-select: none;
-webkit-user-select: none;
cursor: pointer;
display: inline-block;
height: 26px;
margin-right: 15px;
position: relative;
width: 26px;
vertical-align: middle;
}
.player__controls-item--play {
overflow: hidden;
}
.player__controls-item--play .rectangle,
.player__controls-item--play .triangle {
height: 26px;
left: 0;
position: absolute;
top: 0;
width: 26px;
}
.player__controls-item--play .rectangle {
z-index: 5;
}
.player__controls-item--play .rectangle::before,
.player__controls-item--play .rectangle::after {
transition: all 0.3s ease;
background-color: #e31e24;
content: '';
height: 100%;
width: 50%;
}
.player--playing .player__controls-item--play .rectangle::before,
.player--playing .player__controls-item--play .rectangle::after {
width: 30%;
}
.player__controls-item--play .rectangle::before {
float: left;
overflow: hidden;
}
.player__controls-item--play .rectangle::after {
float: right;
}
.player__controls-item--play .triangle {
z-index: 10;
}
.player__controls-item--play .triangle::before,
.player__controls-item--play .triangle::after {
transition: all 0.3s ease;
background-color: transparent;
border-bottom: 13px solid transparent;
border-right: 26px solid #fff;
border-top: 13px solid transparent;
content: '';
height: 0;
position: absolute;
right: 0;
top: 0;
width: 0;
}
.player__controls-item--play .triangle::before {
transform: translate(0, -50%);
}
.player--playing .player__controls-item--play .triangle::before {
transform: translate(0, -100%);
}
.player__controls-item--play .triangle::after {
transform: translate(0, 50%);
}
.player--playing .player__controls-item--play .triangle::after {
transform: translate(0, 100%);
}
.player__controls-item--play:hover .rectangle::before,
.player__controls-item--play:hover .rectangle::after {
background-color: #c1181d;
}
.player__controls-item--next,
.player__controls-item--prev {
width: 32px;
}
.player__controls-item--next::before,
.player__controls-item--next::after,
.player__controls-item--prev::before,
.player__controls-item--prev::after {
transition: all 0.3s ease;
border-bottom: 13px solid transparent;
border-top: 13px solid transparent;
content: '';
height: 0;
position: absolute;
top: 0;
width: 0;
}
.player__controls-item--next::before,
.player__controls-item--next::after {
border-left: 16px solid #e31e24;
}
.player__controls-item--next::before {
left: 0;
}
.player__controls-item--next::after {
left: 10px;
}
.player__controls-item--next:hover::before,
.player__controls-item--next:hover::after {
border-left-color: #c1181d;
}
.player__controls-item--prev::before,
.player__controls-item--prev::after {
border-right: 16px solid #e31e24;
}
.player__controls-item--prev::before {
right: 0;
}
.player__controls-item--prev::after {
right: 10px;
}
.player__controls-item--prev:hover::before,
.player__controls-item--prev:hover::after {
border-right-color: #c1181d;
}
.album {
transform: translateY(-50%);
transition: all var(--transition-time) var(--transition-method);
box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.15);
cursor: pointer;
height: var(--album-size);
position: absolute;
right: 0;
top: 50%;
width: var(--album-size);
z-index: 100;
border-radius: 8px;
}
.player--open .album {
cursor: default;
right: -80px;
}
.album__cover {
background-color: var(--color-black);
background-size: cover;
background-position: center;
height: 100%;
position: relative;
width: 100%;
z-index: 20;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.vinyl {
transition: all var(--transition-time) var(--transition-method);
left: calc(var(--vinyl-offset) / 2);
position: absolute;
top: calc(var(--vinyl-offset) / 2);
height: var(--vinyl-size);
width: var(--vinyl-size);
z-index: 10;
}
.vinyl__shadow,
.vinyl__circle {
border-radius: 100%;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.vinyl__shadow {
box-shadow: 2px 8px 15px rgba(0, 0, 0, 0.2);
z-index: 5;
}
.vinyl__circle {
animation: rotate-vinyl 3s linear infinite both paused;
background-color: #1e1b1b;
background-image: radial-gradient(circle, #2c2727 10%, transparent 10%),
repeating-radial-gradient(circle, #2c2727, #1e1b1b 2px, #2c2727 4px);
z-index: 10;
}
.player--open .vinyl {
left: 0 !important;
}
.player--playing .vinyl {
left: 45% !important;
}
.player--playing .vinyl__circle {
animation-play-state: running;
}
.vinyl__center {
transform: translate(-50%, -50%);
background-color: #292424;
border-radius: 100%;
height: var(--vinyl-center-size);
left: 50%;
position: absolute;
top: 50%;
width: var(--vinyl-center-size);
}
.vinyl__center::before,
.vinyl__center::after {
transform: translate(-50%, -50%);
border-radius: 100%;
content: '';
left: 50%;
position: absolute;
top: 50%;
}
.vinyl__center::before {
background-color: #e31e24;
background-image: var(--center-bg);
background-size: cover;
background-position: center;
height: calc(var(--vinyl-center-size) - 20px);
width: calc(var(--vinyl-center-size) - 20px);
z-index: 5;
}
.vinyl__center::after {
background-color: #fff;
height: 20px;
width: 20px;
z-index: 10;
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
}
@keyframes rotate-vinyl {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.has-fade-in {
transition: all calc(var(--transition-time) * 2) var(--transition-method);
opacity: 0;
pointer-events: none;
}
.player--open .has-fade-in {
opacity: 1;
pointer-events: auto;
}
/* Content styling */
.music-production-content p {
margin-bottom: 1rem;
@@ -167,6 +737,258 @@ article {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Flatten elements when lightbox is open to fix stacking context issues */
body.lightbox-open .player-container,
body.lightbox-open .player {
transform: none !important;
perspective: none !important;
z-index: 1 !important;
}
/* ===== Premium Metadata Table ===== */
.prod-meta-card {
margin: 2.5rem auto;
max-width: 720px;
border-radius: 14px;
overflow: hidden;
box-shadow: 0 4px 24px rgba(30, 34, 40, 0.08);
border: 1px solid rgba(164,174,198,0.18);
background: #fff;
}
.prod-meta-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
.prod-meta-table thead tr {
background: linear-gradient(90deg, #343f52 0%, #4a5568 100%);
color: #fff;
}
.prod-meta-table thead th {
padding: 0.85rem 1.25rem;
text-align: left;
font-weight: 600;
font-size: 0.78rem;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.prod-meta-table thead th i {
margin-right: 0.4rem;
opacity: 0.8;
}
.prod-meta-table tbody tr {
transition: background 0.18s ease;
border-bottom: 1px solid rgba(164,174,198,0.12);
}
.prod-meta-table tbody tr:last-child {
border-bottom: none;
}
.prod-meta-table tbody tr:nth-child(even) {
background: #f8f9fc;
}
.prod-meta-table tbody tr:hover {
background: #eef1f9;
}
.prod-meta-label {
padding: 0.85rem 1.25rem;
font-weight: 600;
color: #343f52;
white-space: nowrap;
width: 36%;
vertical-align: middle;
}
.prod-meta-label i {
margin-right: 0.45rem;
color: #e31e24;
font-size: 1rem;
vertical-align: middle;
}
.prod-meta-value {
padding: 0.85rem 1.25rem;
color: #60697b;
vertical-align: middle;
line-height: 1.6;
}
.prod-meta-badge {
display: inline-block;
padding: 0.2em 0.75em;
border-radius: 50rem;
background: rgba(116, 126, 209, 0.12);
color: #747ed1;
font-size: 0.82rem;
font-weight: 600;
letter-spacing: 0.02em;
}
.prod-platform-btn {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.38rem 1rem;
border-radius: 50rem;
font-size: 0.82rem;
font-weight: 600;
text-decoration: none !important;
transition: all 0.22s ease;
border: 1.5px solid transparent;
}
.prod-platform-btn--spotify {
background: rgba(30, 215, 96, 0.1);
color: #1db954;
border-color: rgba(30, 215, 96, 0.25);
}
.prod-platform-btn--spotify:hover {
background: #1db954;
color: #fff !important;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(30, 215, 96, 0.35);
}
.prod-platform-btn--youtube {
background: rgba(227, 30, 36, 0.08);
color: #e31e24;
border-color: rgba(227, 30, 36, 0.2);
}
.prod-platform-btn--youtube:hover {
background: #e31e24;
color: #fff !important;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(227, 30, 36, 0.35);
}
@media (max-width: 576px) {
.prod-meta-table {
display: block;
overflow-x: auto;
}
.prod-meta-label {
width: auto;
white-space: normal;
}
}
</style>
@endpush
@push('scripts')
<script>
// Load YouTube Iframe Player API dynamically
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var ytPlayer;
var isPlayerReady = false;
window.onYouTubeIframeAPIReady = function() {
ytPlayer = new YT.Player('hidden-youtube-player', {
height: '1',
width: '1',
videoId: "{{ $videoId }}",
playerVars: {
'autoplay': 0,
'controls': 0,
'disablekb': 1,
'fs': 0,
'modestbranding': 1,
'rel': 0,
'showinfo': 0
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
isPlayerReady = true;
// Check if auto-play was active from previous page
const autoPlayActive = localStorage.getItem('vinyl_auto_play') === 'true';
if (autoPlayActive) {
startPlayback();
}
}
function onPlayerStateChange(event) {
const playerEl = document.getElementById('detail-vinyl-player');
if (!playerEl) return;
if (event.data === YT.PlayerState.PLAYING) {
playerEl.classList.add('player--playing');
localStorage.setItem('vinyl_auto_play', 'true');
} else if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.ENDED) {
playerEl.classList.remove('player--playing');
if (event.data === YT.PlayerState.ENDED) {
localStorage.setItem('vinyl_auto_play', 'false');
}
}
}
function startPlayback() {
if (isPlayerReady && ytPlayer && typeof ytPlayer.playVideo === 'function') {
ytPlayer.playVideo();
}
}
function pausePlayback() {
if (isPlayerReady && ytPlayer && typeof ytPlayer.pauseVideo === 'function') {
ytPlayer.pauseVideo();
}
}
document.addEventListener('DOMContentLoaded', function() {
const playBtn = document.getElementById('play-cd-btn');
const playerEl = document.getElementById('detail-vinyl-player');
if (playBtn) {
playBtn.addEventListener('click', function(e) {
e.preventDefault();
if (!isPlayerReady) return;
if (playerEl.classList.contains('player--playing')) {
pausePlayback();
localStorage.setItem('vinyl_auto_play', 'false');
} else {
startPlayback();
localStorage.setItem('vinyl_auto_play', 'true');
}
});
}
// Capture next/prev clicks to persist play state
const prevBtn = document.querySelector('.player__controls-item--prev');
const nextBtn = document.querySelector('.player__controls-item--next');
[prevBtn, nextBtn].forEach(btn => {
if (btn && btn.tagName === 'A') {
btn.addEventListener('click', function() {
if (playerEl && playerEl.classList.contains('player--playing')) {
localStorage.setItem('vinyl_auto_play', 'true');
} else {
localStorage.setItem('vinyl_auto_play', 'false');
}
});
}
});
});
</script>
@endpush
@endsection
@@ -0,0 +1,386 @@
@extends('layouts.site')
@section('title', t('Dijital Dönüşüm (Yazılım Danışmanlığı) | Trunçgil'))
@section('meta_description', t('İşletmenizin dijital dönüşüm yolculuğunda uzman kadromuzla stratejik danışmanlık, süreç otomasyonu, yapay zeka entegrasyonu ve bulut çözümleri sunuyoruz.'))
@push('styles')
<style>
.typer-custom, .typer, .cursor {
color: #e31e24 !important;
}
.hero-gradient-bg {
background: linear-gradient(135deg, #edf2fc 0%, #f4f7fd 100%) !important;
}
.hover-lift {
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) !important;
}
.hover-lift:hover {
transform: translateY(-5px) !important;
box-shadow: 0 1rem 3rem rgba(30, 34, 40, 0.075) !important;
}
.nav-tabs.nav-tabs-bg .nav-link.active {
background-color: #ffffff !important;
box-shadow: 0 0.25rem 1.75rem rgba(30,34,40,.07) !important;
border-color: transparent !important;
}
.bullet-soft-red i {
background-color: #fdebee !important;
color: #e31e24 !important;
}
.bullet-soft-green i {
background-color: #e6f5ec !important;
color: #45c4a0 !important;
}
.bullet-soft-yellow i {
background-color: #feeed9 !important;
color: #fab758 !important;
}
.btn-premium-red {
background-color: #e31e24 !important;
color: #ffffff !important;
border-color: #e31e24 !important;
transition: all 0.3s ease;
}
.btn-premium-red:hover {
background-color: #c1161b !important;
border-color: #c1161b !important;
box-shadow: 0 0.5rem 1.5rem rgba(227, 30, 36, 0.25) !important;
transform: translateY(-2px);
}
.btn-premium-teal {
background-color: #45c4a0 !important;
color: #ffffff !important;
border-color: #45c4a0 !important;
transition: all 0.3s ease;
}
.btn-premium-teal:hover {
background-color: #34a383 !important;
border-color: #34a383 !important;
box-shadow: 0 0.5rem 1.5rem rgba(69, 196, 160, 0.25) !important;
transform: translateY(-2px);
}
</style>
@endpush
@section('content')
<div class="grow shrink-0">
{{-- Section 1: Hero --}}
<section class="wrapper hero-gradient-bg">
<div class="container pt-10 pb-16 xl:pt-[5rem] lg:pt-[5rem] md:pt-[5rem] xl:pb-28 lg:pb-28 md:pb-28">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-35px] lg:mx-[-20px] !mt-[-50px] items-center">
{{-- Left Column: Copywriting --}}
<div class="md:w-10/12 md:!ml-[8.33333333%] lg:!ml-0 lg:w-5/12 xl:!ml-0 xl:w-5/12 w-full flex-[0_0_auto] !px-[15px] xl:!px-[35px] lg:!px-[20px] !mt-[50px] max-w-full lg:!mt-[-.5rem] xl:!mt-[-.5rem] text-center lg:text-left xl:text-left order-2 lg:!order-none xl:!order-none">
<h1 class="xl:!text-[2.6rem] !text-[calc(1.385rem_+_1.62vw)] !leading-[1.1] !font-bold !tracking-normal [word-spacing:normal!important] !mb-5 md:mx-10 lg:mx-0">
{!! t('Geleceğin Teknolojisi ile') !!}<br>
<span class="typer !text-[#e31e24] whitespace-nowrap" data-delay="100" data-words="{{ t('Dijital Dönüşüm') }},{{ t('Stratejik Danışmanlık') }},{{ t('Akıllı Otomasyon') }},{{ t('Bulut Çözümleri') }}"></span>
<span class="cursor !text-[#e31e24]" data-owner="typer"></span>
</h1>
<p class="lead !text-[1.05rem] !leading-[1.6] font-medium !mb-7 text-muted">
{{ t('İşletmenizin dijital olgunluk seviyesini artırıyor; ölçeklenebilir yazılımlar, yapay zeka modelleri ve yeni nesil entegrasyonlar ile pazar liderliğinizi pekiştiriyoruz.') }}
</p>
<div class="flex justify-center lg:!justify-start xl:!justify-start">
<span><a href="{{ route('page.show', ['slug' => 'iletisim']) }}" class="btn btn-lg btn-premium-red rounded !mr-3">{{ t('Teklif Alın') }}</a></span>
<span><a href="#neler-apiyoruz" class="btn btn-lg btn-premium-teal rounded">{{ t('Hizmetlerimiz') }}</a></span>
</div>
</div>
{{-- Right Column: Dynamic Image Grid --}}
<div class="xl:w-7/12 lg:w-7/12 w-full flex-[0_0_auto] !px-[15px] xl:!px-[35px] lg:!px-[20px] !mt-[50px] max-w-full">
<div class="flex flex-wrap mx-[-15px]">
<div class="w-3/12 !ml-[8.33333333%] lg:!ml-0 lg:w-4/12 xl:!ml-0 xl:w-4/12 flex-[0_0_auto] !px-[15px] max-w-full flex flex-col">
<div class="ml-auto !mt-auto"><img class="!max-w-full h-auto rounded-[.6rem] !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]" src="{{ asset('assets/img/photos/sa20.jpg') }}" alt="{{ t('Yazılım Geliştirme') }}"></div>
<div class="ml-auto !mt-5 !mb-10"><img class="!max-w-full h-auto rounded-[.6rem] !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]" src="{{ asset('assets/img/photos/sa18.jpg') }}" alt="{{ t('Bulut Dönüşümü') }}"></div>
</div>
<div class="w-4/12 lg:w-5/12 xl:w-5/12 flex-[0_0_auto] !px-[15px] max-w-full">
<div><img class="w-full max-w-full h-auto rounded-[.6rem] !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]" src="{{ asset('assets/img/photos/sa16.jpg') }}" alt="{{ t('Süreç Analizi') }}"></div>
</div>
<div class="w-3/12 flex-[0_0_auto] !px-[15px] max-w-full flex flex-col">
<div class="!mt-auto"><img class="!max-w-full h-auto rounded-[.6rem] !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]" src="{{ asset('assets/img/photos/sa21.jpg') }}" alt="{{ t('AI Entegrasyonu') }}"></div>
<div class="!mt-5"><img class="!max-w-full h-auto rounded-[.6rem] !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]" src="{{ asset('assets/img/photos/sa19.jpg') }}" alt="{{ t('Yazılım Mimarisi') }}"></div>
<div class="!mt-5 !mb-10"><img class="!max-w-full h-auto rounded-[.6rem] !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]" src="{{ asset('assets/img/photos/sa17.jpg') }}" alt="{{ t('Siber Güvenlik') }}"></div>
</div>
</div>
</div>
</div>
</div>
</section>
{{-- Section 2: Clients --}}
@php
$partnersPage = \App\Models\Page::where('slug', 'cozum-ortaklari')->first();
$partners = [];
if ($partnersPage && !empty($partnersPage->data['partners'])) {
$partners = array_slice($partnersPage->data['partners'], 0, 5);
}
@endphp
@if(!empty($partners))
<section class="wrapper !bg-[#ffffff]">
<div class="container py-8 border-b">
<h2 class="!text-[.75rem] !leading-[1.35] font-bold [word-spacing:0.1rem] !tracking-[0.02rem] uppercase !text-[#aab0bc] !text-center !mb-6">{!! t('GÜVENİLİR ÇÖZÜM ORTAKLARIMIZLA BİRLİKTE BAŞARDIK') !!}</h2>
<div class="flex flex-wrap mx-0 md:mx-[-20px] lg:mx-[-20px] xl:mx-[-35px] !mt-[-20px] justify-center items-center opacity-70">
@foreach($partners as $partner)
<div class="w-4/12 xl:w-2/12 lg:w-2/12 md:w-2/12 flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] md:!px-[20px] !mt-[20px] max-w-full text-center">
<a href="{{ $partner['url'] ?? '#' }}" target="_blank" title="{{ $partner['name'] ?? '' }}">
<figure class="px-5 md:!px-0 lg:!px-2 xl:!px-3 xxl:!px-4" style="height: 75px; display: flex; align-items: center; justify-content: center;">
<img src="{{ asset('storage/' . $partner['logo']) }}" alt="{{ $partner['name'] ?? '' }}" style="max-height: 68px; width: auto; object-fit: contain;">
</figure>
</a>
</div>
@endforeach
</div>
</div>
</section>
@endif
{{-- Section 3: Value Pillars & Interactive Tabs --}}
<section class="wrapper !bg-[#ffffff]" id="neler-apiyoruz">
<div class="container py-20 xl:py-28 lg:py-28 md:py-28">
<div class="flex flex-wrap mx-[-15px]">
<div class="md:w-10/12 md:!ml-[8.33333333%] lg:w-8/12 lg:!ml-[16.66666667%] xl:w-8/12 xl:!ml-[16.66666667%] w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto !text-center">
<h2 class="!text-[.75rem] uppercase !text-[#aab0bc] !mb-3 !tracking-[0.02rem] !leading-[1.35]">{{ t('HİZMET ALANLARIMIZ') }}</h2>
<h3 class="xl:!text-[2rem] !text-[calc(1.325rem_+_0.9vw)] !leading-[1.2] !font-bold !tracking-normal [word-spacing:normal!important] !mb-10 xl:!px-10 xxl:!px-20">{{ t('Uçtan Uca Dijital Dönüşüm Stratejileri') }}</h3>
</div>
</div>
{{-- Nav Tabs --}}
<ul class="nav nav-tabs nav-tabs-bg flex justify-between nav-justified xl:!flex-row lg:!flex-row flex-col !mb-8">
<li class="nav-item !mb-[20px]">
<a class="nav-link !flex flex-row hover-lift active" data-bs-toggle="tab" href="#tab-strateji">
<div class="icon btn btn-block btn-lg btn-soft-red pointer-events-none !mr-4 !p-3">
<i class="uil uil-presentation-play text-[1.4rem]"></i>
</div>
<div>
<h4 class="!mb-[.25rem] !text-[0.95rem]">{{ t('Stratejik Planlama') }}</h4>
<p class="text-muted !text-[0.75rem]">{{ t('Yol haritası & mimari tasarım') }}</p>
</div>
</a>
</li>
<li class="nav-item !mb-[20px]">
<a class="nav-link !flex flex-row hover-lift" data-bs-toggle="tab" href="#tab-otomasyon">
<div class="icon btn btn-block btn-lg btn-soft-green pointer-events-none !mr-4 !p-3">
<i class="uil uil-processor text-[1.4rem]"></i>
</div>
<div>
<h4 class="!mb-[.25rem] !text-[0.95rem]">{{ t('Akıllı Otomasyon') }}</h4>
<p class="text-muted !text-[0.75rem]">{{ t('Hızlı, hatasız ve optimize süreçler') }}</p>
</div>
</a>
</li>
<li class="nav-item !mb-[20px]">
<a class="nav-link !flex flex-row hover-lift" data-bs-toggle="tab" href="#tab-guvenlik">
<div class="icon btn btn-block btn-lg btn-soft-yellow pointer-events-none !mr-4 !p-3">
<i class="uil uil-shield-check text-[1.4rem]"></i>
</div>
<div>
<h4 class="!mb-[.25rem] !text-[0.95rem]">{{ t('Siber Güvenlik & Bulut') }}</h4>
<p class="text-muted !text-[0.75rem]">{{ t('Yüksek performans & güvenli altyapı') }}</p>
</div>
</a>
</li>
</ul>
{{-- Tab Contents --}}
<div class="tab-content !mt-10">
{{-- Tab 1: Strategy --}}
<div class="tab-pane fade show active" id="tab-strateji">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-35px] lg:mx-[-20px] !mt-[-50px] items-center">
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] max-w-full !mt-[50px]">
<div class="flex flex-wrap mx-[-12.5px] !mt-[-25px] items-center">
<div class="w-6/12 flex-[0_0_auto] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !px-[15px] max-w-full !mt-[25px]">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] flex !ml-auto" src="{{ asset('assets/img/photos/sa13.jpg') }}" alt="{{ t('Analiz') }}">
</div>
<div class="w-6/12 flex-[0_0_auto] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !px-[15px] max-w-full !mt-[25px]">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] !mb-5" src="{{ asset('assets/img/photos/sa14.jpg') }}" alt="{{ t('Toplantı') }}">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] flex !w-10/12 flex-[0_0_auto]" src="{{ asset('assets/img/photos/sa15.jpg') }}" alt="{{ t('Karar') }}">
</div>
</div>
</div>
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] max-w-full !mt-[50px]">
<h2 class="!mb-3 !leading-[1.35]">{{ t('Teknolojik Yol Haritanızı Çiziyoruz') }}</h2>
<p class="!mb-6">{{ t('İşletmenizin mevcut dijital yapısını derinlemesine inceliyor, darboğazları belirliyor ve büyüme hedeflerinize en uygun teknoloji yığınını seçiyoruz.') }}</p>
<ul class="pl-0 list-none bullet-bg bullet-soft-red !mb-6">
<li class="relative !pl-6 !mb-2"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Dijital Olgunluk Seviyesi Analizi') }}</li>
<li class="relative !pl-6 !mb-2"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Doğru Teknoloji Seçimi & Mimari Tasarım') }}</li>
<li class="relative !pl-6"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Geleceğe Hazır, Ölçeklenebilir Yol Haritası') }}</li>
</ul>
<a href="{{ route('page.show', ['slug' => 'iletisim']) }}" class="btn btn-premium-red rounded">{{ t('Danışmanlık Alın') }}</a>
</div>
</div>
</div>
{{-- Tab 2: Automation --}}
<div class="tab-pane fade" id="tab-otomasyon">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-35px] lg:mx-[-20px] !mt-[-50px] items-center">
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] max-w-full !mt-[50px] xl:!order-2 lg:!order-2">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-12.5px] lg:mx-[-12.5px] md:mx-[-12.5px] !mt-[-25px]">
<div class="w-5/12 flex-[0_0_auto] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !px-[15px] max-w-full !mt-[25px]">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] my-5 flex !ml-auto" src="{{ asset('assets/img/photos/sa9.jpg') }}" alt="{{ t('Kodlama') }}">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] flex !w-10/12 flex-[0_0_auto] !ml-auto" src="{{ asset('assets/img/photos/sa10.jpg') }}" alt="{{ t('Kontrol') }}">
</div>
<div class="w-7/12 flex-[0_0_auto] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !px-[15px] max-w-full !mt-[25px]">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] !mb-5" src="{{ asset('assets/img/photos/sa11.jpg') }}" alt="{{ t('Entegrasyon') }}">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] flex w-11/12 flex-[0_0_auto]" src="{{ asset('assets/img/photos/sa12.jpg') }}" alt="{{ t('Başarı') }}">
</div>
</div>
</div>
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] max-w-full !mt-[50px]">
<h2 class="!mb-3 !leading-[1.35]">{{ t('Manuel Süreçlerinizi Otomatikleştirin') }}</h2>
<p class="!mb-6">{{ t('Hata oranlarını sıfıra indirmek ve operasyonel hızı katlamak için iş akışlarınızı dijital sistemler ile uçtan uca otomatik hale getiriyoruz.') }}</p>
<ul class="pl-0 list-none bullet-bg bullet-soft-green !mb-6">
<li class="relative !pl-6 !mb-2"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Yapay Zeka Destekli Süreç Yönetimi (RPA)') }}</li>
<li class="relative !pl-6 !mb-2"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Kusursuz ve Hızlı Veri Akışı Entegrasyonları') }}</li>
<li class="relative !pl-6"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Özel Geliştirilmiş Web ve Mobil ERP/CRM Arayüzleri') }}</li>
</ul>
<a href="{{ route('page.show', ['slug' => 'iletisim']) }}" class="btn btn-premium-teal rounded">{{ t('Hemen Başlayın') }}</a>
</div>
</div>
</div>
{{-- Tab 3: Security & Cloud --}}
<div class="tab-pane fade" id="tab-guvenlik">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-35px] lg:mx-[-20px] !mt-[-50px] items-center">
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] max-w-full !mt-[50px]">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-12.5px] lg:mx-[-12.5px] md:mx-[-12.5px] !mt-[-25px]">
<div class="w-6/12 flex-[0_0_auto] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !px-[15px] max-w-full !mt-[25px]">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] !mb-5" src="{{ asset('assets/img/photos/sa5.jpg') }}" alt="{{ t('Siber Güvenlik') }}">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] flex !w-10/12 flex-[0_0_auto] !ml-auto" src="{{ asset('assets/img/photos/sa6.jpg') }}" alt="{{ t('Gözlem') }}">
</div>
<div class="w-6/12 flex-[0_0_auto] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !px-[15px] max-w-full !mt-[25px]">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] my-5" src="{{ asset('assets/img/photos/sa7.jpg') }}" alt="{{ t('Cloud') }}">
<img class="max-w-full h-auto rounded !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] flex !w-10/12 flex-[0_0_auto]" src="{{ asset('assets/img/photos/sa8.jpg') }}" alt="{{ t('Tasarım') }}">
</div>
</div>
</div>
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] xl:!px-[35px] lg:!px-[20px] !px-[15px] max-w-full !mt-[50px]">
<h2 class="!mb-3 !leading-[1.35]">{{ t('Verilerinizi Buluta Güvenle Taşıyın') }}</h2>
<p class="!mb-6">{{ t('Güvenli, yedekli ve yüksek performanslı bulut native altyapılar tasarlıyor, ISO27001 standartlarında siber güvenlik denetimleri yapıyoruz.') }}</p>
<ul class="pl-0 list-none bullet-bg bullet-soft-yellow !mb-6">
<li class="relative !pl-6 !mb-2"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Cloud Native Mimariler (AWS, Azure, GCP)') }}</li>
<li class="relative !pl-6 !mb-2"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Sızma Testleri & Güvenlik Duvarı Entegrasyonları') }}</li>
<li class="relative !pl-6"><i class="uil uil-check absolute left-0 w-4 h-4 text-[0.8rem] leading-none !tracking-[normal] !text-center flex items-center justify-center rounded-[100%] top-[0.2rem] before:content-['\e9dd'] before:align-middle"></i>{{ t('Uptime & Performans Optimizasyonları') }}</li>
</ul>
<a href="{{ route('page.show', ['slug' => 'iletisim']) }}" class="btn btn-premium-teal rounded">{{ t('Hemen İletişime Geçin') }}</a>
</div>
</div>
</div>
</div>
</div>
</section>
{{-- Section 4: Success Statistics / Fact Counters --}}
<section class="wrapper hero-gradient-bg">
<div class="container py-16 xl:py-24 lg:py-24 md:py-24">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-35px] lg:mx-[-20px] !mt-[-50px] xl:!mt-0 lg:!mt-0 items-center">
<div class="xl:w-4/12 lg:w-4/12 w-full flex-[0_0_auto] !px-[15px] xl:!px-[35px] lg:!px-[20px] !mt-[50px] xl:!mt-0 lg:!mt-0 max-w-full">
<h2 class="!text-[0.8rem] uppercase inline-flex !leading-[1.35] text-line relative align-top !pl-[1.4rem] !tracking-[0.02rem] before:content-[''] before:absolute before:inline-block before:translate-y-[-60%] before:w-3 before:h-[0.05rem] before:left-0 before:top-2/4 before:bg-[#e31e24] !text-[#e31e24] !mb-3">{{ t('Başarı Karnemiz') }}</h2>
<h3 class="!text-[calc(1.315rem_+_0.78vw)] font-bold xl:!text-[1.9rem] !leading-[1.25] !mb-0 xxl:!pr-10">{{ t('Rakamlarla Dijital Dönüşüm Gücü') }}</h3>
</div>
<div class="lg:w-8/12 xl:w-8/12 w-full flex-[0_0_auto] !px-[15px] xl:!px-[35px] lg:!px-[20px] !mt-[50px] max-w-full xl:!mt-2 lg:!mt-2">
<div class="flex flex-wrap mx-[-15px] items-center counter-wrapper !mt-[-30px] !text-center">
<div class="xl:w-4/12 lg:w-4/12 md:w-4/12 w-full flex-[0_0_auto] !px-[15px] !mt-[30px] max-w-full">
<h3 class="counter counter-lg !text-[calc(1.35rem_+_1.2vw)] !tracking-[normal] !leading-none !mb-[.5rem] xl:!text-[2.25rem]">%40+</h3>
<p class="font-medium !mb-0">{{ t('Operasyonel Maliyet Düşüşü') }}</p>
</div>
<div class="xl:w-4/12 lg:w-4/12 md:w-4/12 w-full flex-[0_0_auto] !px-[15px] !mt-[30px] max-w-full">
<h3 class="counter counter-lg !text-[calc(1.35rem_+_1.2vw)] !tracking-[normal] !leading-none !mb-[.5rem] xl:!text-[2.25rem]">{{ t('3 Kat') }}</h3>
<p class="font-medium !mb-0">{{ t('Hız ve Verimlilik Artışı') }}</p>
</div>
<div class="xl:w-4/12 lg:w-4/12 md:w-4/12 w-full flex-[0_0_auto] !px-[15px] !mt-[30px] max-w-full">
<h3 class="counter counter-lg !text-[calc(1.35rem_+_1.2vw)] !tracking-[normal] !leading-none !mb-[.5rem] xl:!text-[2.25rem]">%99.9</h3>
<p class="font-medium !mb-0">{{ t('Sistem Ayakta Kalma Oranı') }}</p>
</div>
</div>
</div>
</div>
</div>
</section>
{{-- Section 5: Happy Customers / Testimonials --}}
<section class="wrapper !bg-[#ffffff]">
<div class="container py-20 xl:py-28 lg:py-28 md:py-28">
<div class="flex flex-wrap mx-[-15px]">
<div class="md:w-10/12 md:!ml-[8.33333333%] lg:w-8/12 lg:!ml-[16.66666667%] xl:w-8/12 xl:!ml-[16.66666667%] w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto !text-center">
<h2 class="!text-[.75rem] uppercase !text-[#aab0bc] !mb-3 !tracking-[0.02rem] !leading-[1.35]">{{ t('MÜŞTERİ YORUMLARI') }}</h2>
<h3 class="xl:!text-[2rem] !text-[calc(1.325rem_+_0.9vw)] !leading-[1.2] !font-bold !tracking-normal [word-spacing:normal!important] !mb-10">{{ t('İş Ortaklarımızın Başarı Hikayeleri') }}</h3>
</div>
</div>
<div class="flex flex-wrap mx-[-15px] !mt-[-30px]">
{{-- Testimonial 1 --}}
<div class="md:w-6/12 lg:w-6/12 xl:w-4/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mt-[30px]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] hover-lift">
<div class="card-body flex-[1_1_auto] p-[40px]">
<span class="ratings inline-block relative w-20 h-[0.8rem] text-[0.9rem] leading-none before:text-[rgba(38,43,50,0.1)] after:inline-block after:not-italic after:font-normal after:absolute after:!text-[#fcc032] after:content-['\2605\2605\2605\2605\2605'] after:overflow-hidden after:left-0 after:top-0 before:inline-block before:not-italic before:font-normal before:absolute before:!text-[#fcc032] before:content-['\2605\2605\2605\2605\2605'] before:overflow-hidden before:left-0 before:top-0 five !mb-3"></span>
<blockquote class="!text-[0.85rem] !leading-[1.7] font-medium !pl-0 relative border-0">
<p class="!mb-6">{{ t('“Trunçgil ekibi sayesinde tüm üretim ve sipariş süreçlerimizi tek bir ERP sisteminde topladık. Manuel hatalarımız sıfıra indi ve verimliliğimiz inanılmaz arttı!”') }}</p>
<div class="flex items-center text-left">
<div class="info !pl-0">
<h5 class="!mb-1 text-[.95rem] !leading-[1.5]">Kerem Can Azak</h5>
<p class="!mb-0 !text-[.8rem] text-muted">Stellar Construction</p>
</div>
</div>
</blockquote>
</div>
</div>
</div>
{{-- Testimonial 2 --}}
<div class="md:w-6/12 lg:w-6/12 xl:w-4/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mt-[30px]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] hover-lift">
<div class="card-body flex-[1_1_auto] p-[40px]">
<span class="ratings inline-block relative w-20 h-[0.8rem] text-[0.9rem] leading-none before:text-[rgba(38,43,50,0.1)] after:inline-block after:not-italic after:font-normal after:absolute after:!text-[#fcc032] after:content-['\2605\2605\2605\2605\2605'] after:overflow-hidden after:left-0 after:top-0 before:inline-block before:not-italic before:font-normal before:absolute before:!text-[#fcc032] before:content-['\2605\2605\2605\2605\2605'] before:overflow-hidden before:left-0 before:top-0 five !mb-3"></span>
<blockquote class="!text-[0.85rem] !leading-[1.7] font-medium !pl-0 relative border-0">
<p class="!mb-6">{{ t('“Altyapımızı bulut mimarisine geçirirken sundukları stratejik danışmanlık hizmeti mükemmeldi. Sıfır kesintiyle tüm geçişi tamamladık. Kesinlikle tavsiye ediyoruz.”') }}</p>
<div class="flex items-center text-left">
<div class="info !pl-0">
<h5 class="!mb-1 text-[.95rem] !leading-[1.5]">Ahmet Cezmi Savaş</h5>
<p class="!mb-0 !text-[.8rem] text-muted">Rhapsode Yayınevi</p>
</div>
</div>
</blockquote>
</div>
</div>
</div>
{{-- Testimonial 3 --}}
<div class="md:w-6/12 lg:w-6/12 xl:w-4/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mt-[30px] max-md:hidden">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] hover-lift">
<div class="card-body flex-[1_1_auto] p-[40px]">
<span class="ratings inline-block relative w-20 h-[0.8rem] text-[0.9rem] leading-none before:text-[rgba(38,43,50,0.1)] after:inline-block after:not-italic after:font-normal after:absolute after:!text-[#fcc032] after:content-['\2605\2605\2605\2605\2605'] after:overflow-hidden after:left-0 after:top-0 before:inline-block before:not-italic before:font-normal before:absolute before:!text-[#fcc032] before:content-['\2605\2605\2605\2605\2605'] before:overflow-hidden before:left-0 before:top-0 five !mb-3"></span>
<blockquote class="!text-[0.85rem] !leading-[1.7] font-medium !pl-0 relative border-0">
<p class="!mb-6">{{ t('“Yapay zeka modellerinin entegrasyonu aşamasındaki bilgileri ve profesyonel yaklaşımları takdire şayandı. Müşteri analizlerimizde büyük yol kat ettik.”') }}</p>
<div class="flex items-center text-left">
<div class="info !pl-0">
<h5 class="!mb-1 text-[.95rem] !leading-[1.5]">Servet Demir</h5>
<p class="!mb-0 !text-[.8rem] text-muted">Dijimind Akademi</p>
</div>
</div>
</blockquote>
</div>
</div>
</div>
</div>
</div>
</section>
{{-- Section 6: Action Banner --}}
<section class="wrapper !bg-[#e31e24] text-white">
<div class="container py-14 text-center">
<div class="flex flex-wrap mx-[-15px] justify-center">
<div class="xl:w-10/12 w-full px-[15px]">
<h3 class="!text-[calc(1.3rem_+_0.6vw)] xl:!text-[1.75rem] font-bold !leading-[1.3] text-white !mb-4">{{ t('İşletmenizi Geleceğe Birlikte Taşıyalım') }}</h3>
<p class="lead !text-[1.05rem] !leading-[1.6] !mb-6 text-white-50">{{ t('Dijital dönüşüm stratejinizi oluşturmak ve size özel yazılım çözümleri üretmek için hazırız.') }}</p>
<a href="{{ route('page.show', ['slug' => 'iletisim']) }}" class="btn btn-white !rounded-[50rem] !mb-0">{{ t('Hemen Başvurun') }}</a>
</div>
</div>
</div>
</section>
</div>
@endsection
+2 -1
View File
@@ -14,7 +14,8 @@
<link rel="preload" href="{{ asset('html/assets/css/fonts/urbanist.css') }}" as="style" onload="this.rel='stylesheet'">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=translate">
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=translate&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=translate&display=swap" rel="stylesheet"></noscript>
@stack('styles')
<style>
.navbar.navbar-light.fixed .btn:not(.btn-expand):not(.btn-gradient) {
+33 -3
View File
@@ -17,16 +17,35 @@ $favicon_path = setting('site_favicon');
}
@endphp
@php
$seo_canonical = $meta['canonical'] ?? url()->current();
$seo_canonical = $meta['canonical'] ?? null;
if (!$seo_canonical) {
$seo_canonical = url()->current();
$localeQuery = request()->query('locale') ?? request()->query('lang');
if ($localeQuery && function_exists('available_language_codes') && in_array($localeQuery, available_language_codes())) {
$seo_canonical .= '?locale=' . $localeQuery;
}
}
$seo_robots = $meta['robots'] ?? 'index, follow';
$seo_og_type = $meta['og_type'] ?? 'website';
$seo_og_locale = str_replace('_', '-', $meta['locale'] ?? app()->getLocale());
$activeLanguages = class_exists(\App\Models\Language::class)
? \App\Models\Language::where('is_active', true)->get()
: collect([]);
$currentUrl = url()->current();
@endphp
<title>{{ $seo_title }}</title>
<meta name="description" content="{{ $seo_description }}">
<meta name="robots" content="{{ $seo_robots }}">
<link rel="canonical" href="{{ $seo_canonical }}">
@foreach($activeLanguages as $lang)
<link rel="alternate" hreflang="{{ $lang->code }}" href="{{ $currentUrl . '?locale=' . $lang->code }}">
@endforeach
@if($activeLanguages->count() > 0)
<link rel="alternate" hreflang="x-default" href="{{ $currentUrl }}">
@endif
<!-- Open Graph / Facebook -->
<meta property="og:type" content="{{ $seo_og_type }}">
<meta property="og:url" content="{{ $seo_canonical }}">
@@ -58,6 +77,15 @@ $favicon_path = setting('site_favicon');
<link rel="icon" href="{{ $favicon_path ? (str_starts_with($favicon_path, 'http') ? $favicon_path : asset($favicon_path)) : asset('assets/img/favicon.png') }}">
@stack('head')
@if(request()->routeIs('homepage'))
@php
$hero_bg_preload = setting('hero_bg') ?: './assets/img/photos/bg16.webp';
if ($hero_bg_preload && !str_starts_with($hero_bg_preload, 'http')) {
$hero_bg_preload = asset($hero_bg_preload);
}
@endphp
<link rel="preload" href="{{ $hero_bg_preload }}" as="image" fetchpriority="high">
@endif
<link rel="stylesheet" href="{{ asset('html/style.css') }}">
<link rel="preload" href="{{ asset('assets/css/icon.css') }}" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ asset('assets/css/icon.css') }}"></noscript>
@@ -70,8 +98,10 @@ $favicon_path = setting('site_favicon');
<link rel="preload" href="{{ asset('assets/css/fonts/space.css') }}" as="style" onload="this.rel='stylesheet'">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@600&family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=translate">
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Caveat:wght@600&family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link href="https://fonts.googleapis.com/css2?family=Caveat:wght@600&family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet"></noscript>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=translate&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=translate&display=swap" rel="stylesheet"></noscript>
<!-- Site Verification -->
@if(setting('seo_google_search_console'))
@@ -0,0 +1,263 @@
<div
x-data="{
githubRepoUrl: @js($this->githubRepo),
commitDays: [],
activeDayIndex: 0,
loading: false,
errorMsg: '',
isEmpty: false,
init() {
this.loadCommits();
},
loadCommits() {
if (!this.githubRepoUrl) {
this.isEmpty = true;
return;
}
this.loading = true;
this.errorMsg = '';
this.isEmpty = false;
this.commitDays = [];
let repoClean = this.githubRepoUrl.replace(/https?:\/\/(www\.)?github\.com\//i, '');
repoClean = repoClean.replace(/\/$/, '');
const parts = repoClean.split('/');
if (parts.length < 2) {
this.loading = false;
this.errorMsg = 'Github depo URL\'si çözümlenemedi. Geçerli format: https://github.com/kullanici/depo';
return;
}
const owner = parts[0];
const repo = parts[1].replace(/\.git$/i, '');
fetch('https://api.github.com/repos/' + owner + '/' + repo + '/commits?per_page=100')
.then(response => {
if (!response.ok) {
if (response.status === 404) throw new Error('Depo bulunamadı veya private (gizli). Deponuzun public (herkese açık) olduğundan emin olun.');
else if (response.status === 403) throw new Error('GitHub API istek limiti aşıldı. Lütfen daha sonra tekrar deneyin.');
else throw new Error('GitHub API hatası: ' + response.statusText);
}
return response.json();
})
.then(commits => {
this.loading = false;
if (!Array.isArray(commits) || commits.length === 0) {
this.isEmpty = true;
return;
}
commits.reverse();
const grouped = {};
commits.forEach(item => {
const dateStr = item.commit.author.date;
if (dateStr) {
const dateOnly = dateStr.substring(0, 10);
if (!grouped[dateOnly]) grouped[dateOnly] = [];
grouped[dateOnly].push(item);
}
});
const sortedDates = Object.keys(grouped).sort();
this.commitDays = sortedDates.map((date, index) => {
const dp = date.split('-');
return {
date: date,
dayNum: index + 1,
formattedDate: dp[2] + '.' + dp[1] + '.' + dp[0],
commits: grouped[date].map(c => ({
message: c.commit.message,
time: new Date(c.commit.author.date).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }),
sha: c.sha.substring(0, 7),
url: c.html_url
}))
};
});
if (this.commitDays.length === 0) {
this.isEmpty = true;
return;
}
this.activeDayIndex = 0;
})
.catch(err => {
this.loading = false;
this.errorMsg = err.message || 'Bir hata oluştu.';
});
},
get activeDay() {
return this.commitDays[this.activeDayIndex] || null;
},
navigateDay(dir) {
const n = this.activeDayIndex + dir;
if (n >= 0 && n < this.commitDays.length) this.activeDayIndex = n;
},
selectDay(i) {
this.activeDayIndex = i;
}
}"
style="margin-top: 1rem; font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;"
>
<style>
.jt-day-btn {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.jt-day-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
}
.jt-arrow-btn {
transition: all 0.2s ease;
}
.jt-arrow-btn:hover:not(:disabled) {
transform: scale(1.08);
background: #eff6ff;
border-color: #93c5fd;
}
.jt-arrow-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.jt-event-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.jt-event-card:hover {
transform: translateY(-2px) translateX(3px);
box-shadow: 0 12px 24px -8px rgba(59, 130, 246, 0.12);
border-color: #bfdbfe !important;
background: #ffffff !important;
}
.jt-timeline-dot {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.jt-event-row:hover .jt-timeline-dot {
transform: scale(1.2);
background-color: #3b82f6 !important;
box-shadow: 0 0 0 5px rgba(59, 130, 246, 0.18);
}
.jt-event-row:hover .jt-timeline-dot-inner {
background-color: #ffffff !important;
}
.jt-sha-link {
transition: all 0.25s ease;
}
.jt-sha-link:hover {
background: #3b82f6 !important;
color: #ffffff !important;
border-color: #3b82f6 !important;
}
.jt-paginator-scroll::-webkit-scrollbar {
height: 4px;
}
.jt-paginator-scroll::-webkit-scrollbar-track {
background: transparent;
}
.jt-paginator-scroll::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 9999px;
}
.jt-paginator-scroll {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
}
@keyframes jt-spin {
to { transform: rotate(360deg); }
}
@keyframes jt-fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.jt-fade-in {
animation: jt-fadeIn 0.4s ease-out;
}
</style>
{{-- Loader --}}
<div x-show="loading" x-cloak style="padding: 3rem 0; display: flex; flex-direction: column; align-items: center; justify-content: center;">
<div style="width: 2.5rem; height: 2.5rem; border: 2px solid #e2e8f0; border-top-color: #3b82f6; border-radius: 50%; animation: jt-spin 0.8s linear infinite; margin-bottom: 0.75rem;"></div>
<span style="font-size: 0.75rem; color: #94a3b8; font-weight: 600; letter-spacing: 0.025em;">Commit geçmişi yükleniyor...</span>
</div>
{{-- Error --}}
<div x-show="errorMsg" x-cloak x-text="errorMsg" style="padding: 1rem 1.25rem; border-radius: 1rem; background: #fef2f2; border: 1px solid #fecaca; color: #dc2626; font-size: 0.75rem; font-weight: 700;"></div>
{{-- Empty --}}
<div x-show="isEmpty && !loading" x-cloak style="padding: 2rem; border-radius: 1.5rem; border: 1px solid #f1f5f9; background: #ffffff; text-align: center; color: #94a3b8; font-style: italic; font-size: 0.875rem;">
Henüz hiçbir commit verisi bulunamadı veya depo adresi yapılandırılmadı.
</div>
{{-- Day Paginator --}}
<div x-show="commitDays.length > 0" x-cloak style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem; background: rgba(255,255,255,0.8); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid #e2e8f0; border-radius: 1.5rem; margin-bottom: 1.75rem; user-select: none; box-shadow: 0 1px 3px rgba(0,0,0,0.04);">
<button type="button" @click="navigateDay(-1)" :disabled="activeDayIndex === 0" class="jt-arrow-btn" style="width: 2.75rem; height: 2.75rem; border-radius: 0.875rem; background: #ffffff; color: #64748b; border: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
</button>
<div class="jt-paginator-scroll" style="flex: 1; overflow-x: auto; padding: 0.375rem 0;">
<div style="display: flex; gap: 0.625rem; justify-content: center; min-width: max-content; padding: 0 0.5rem;">
<template x-for="(day, index) in commitDays" :key="day.date">
<button type="button" @click="selectDay(index)" class="jt-day-btn" :style="index === activeDayIndex
? 'padding: 0.5rem 1.25rem; border-radius: 0.875rem; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 1.5px solid #93c5fd; background: linear-gradient(135deg, #eff6ff, #dbeafe); color: #1d4ed8; font-weight: 800; box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15);'
: 'padding: 0.5rem 1.25rem; border-radius: 0.875rem; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 1px solid #e2e8f0; background: #ffffff; color: #64748b; font-weight: 600;'">
<span style="font-size: 0.75rem; line-height: 1.2;" x-text="day.dayNum + '. Gün'"></span>
<span style="font-size: 0.6rem; margin-top: 0.125rem; opacity: 0.7; font-weight: 500;" x-text="day.formattedDate"></span>
</button>
</template>
</div>
</div>
<button type="button" @click="navigateDay(1)" :disabled="activeDayIndex === commitDays.length - 1" class="jt-arrow-btn" style="width: 2.75rem; height: 2.75rem; border-radius: 0.875rem; background: #ffffff; color: #64748b; border: 1px solid #e2e8f0; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.04);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
{{-- Active Day Card --}}
<div x-show="activeDay" x-cloak class="jt-fade-in" :key="activeDayIndex" style="background: #ffffff; border: 1px solid #f1f5f9; border-radius: 1.5rem; padding: 1.75rem 2rem; box-shadow: 0 4px 20px -4px rgba(15, 23, 42, 0.06);">
{{-- Day Header --}}
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.75rem; padding-bottom: 1.25rem; border-bottom: 1px solid #f1f5f9;">
<div style="display: flex; align-items: center; gap: 0.875rem;">
<span style="font-size: 0.875rem; font-weight: 800; color: #2563eb; background: linear-gradient(135deg, #eff6ff, #dbeafe); padding: 0.5rem 1.125rem; border-radius: 1rem; box-shadow: 0 1px 3px rgba(37, 99, 235, 0.1);" x-text="activeDay ? activeDay.dayNum + '. Gün' : ''"></span>
<span style="font-size: 0.8125rem; font-weight: 600; color: #94a3b8; display: flex; align-items: center; gap: 0.375rem;">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: #60a5fa;"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<span x-text="activeDay ? activeDay.formattedDate : ''"></span>
</span>
</div>
<span style="font-size: 0.6875rem; font-weight: 700; color: #94a3b8; background: #f8fafc; border: 1px solid #f1f5f9; padding: 0.375rem 0.875rem; border-radius: 0.625rem;" x-text="activeDay ? activeDay.commits.length + ' Commit' : ''"></span>
</div>
{{-- Timeline --}}
<div style="position: relative; padding-left: 2.25rem;">
{{-- Gradient Timeline Line --}}
<div style="position: absolute; left: 11px; top: 8px; bottom: 8px; width: 2px; background: linear-gradient(180deg, #3b82f6 0%, #818cf8 40%, #c7d2fe 100%); border-radius: 2px;"></div>
<div style="display: flex; flex-direction: column; gap: 1.25rem;">
<template x-for="(commit, ci) in (activeDay ? activeDay.commits : [])" :key="ci">
<div class="jt-event-row" style="position: relative;">
{{-- Timeline Dot --}}
<div class="jt-timeline-dot" style="position: absolute; left: -32px; top: 6px; width: 24px; height: 24px; border-radius: 50%; background: #ffffff; border: 2.5px solid #3b82f6; display: flex; align-items: center; justify-content: center; z-index: 1;">
<div class="jt-timeline-dot-inner" style="width: 8px; height: 8px; border-radius: 50%; background: #3b82f6;"></div>
</div>
{{-- Event Card --}}
<div class="jt-event-card" style="padding: 1.125rem 1.25rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.03);">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.625rem;">
<span style="font-size: 0.75rem; font-weight: 700; color: #94a3b8; display: flex; align-items: center; gap: 0.375rem;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#60a5fa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<span x-text="commit.time"></span>
</span>
<a :href="commit.url" target="_blank" class="jt-sha-link" x-text="commit.sha" style="font-size: 0.6875rem; font-family: 'SF Mono', 'Fira Code', monospace; font-weight: 700; color: #2563eb; background: #eff6ff; padding: 0.25rem 0.75rem; border-radius: 0.5rem; border: 1px solid #bfdbfe; text-decoration: none;"></a>
</div>
<p style="font-size: 0.8125rem; font-weight: 600; color: #334155; line-height: 1.6; white-space: pre-line; margin: 0;" x-text="commit.message"></p>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
+3 -44
View File
@@ -77,14 +77,7 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
<div>
<a href="mailto:{{setting('contact_email')}}" class="link-inverse">{{setting('contact_email')}}</a>
<br> <a href="tel:{{setting('contact_phone')}}">{{setting('contact_phone')}}</a> <br>
<nav class="nav social social-white !mt-4">
<?php $social_media = json_decode(setting('social_links'), true); ?>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Twitter']) ? $social_media['Twitter'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}"><i class="uil uil-twitter before:content-['\ed59'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Facebook']) ? $social_media['Facebook'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Facebook') }}"><i class="uil uil-facebook-f before:content-['\eae2'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Github']) ? $social_media['Github'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}"><i class="uil uil-github before:content-['\eb40'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Instagram']) ? $social_media['Instagram'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}"><i class="uil uil-instagram before:content-['\eb9c'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Youtube']) ? $social_media['Youtube'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('YouTube') }}"><i class="uil uil-youtube before:content-['\edb5'] !text-white text-[1rem]" aria-hidden="true"></i></a>
</nav>
</div>
</div>
</div>
@@ -113,34 +106,7 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
<!-- Social Media & Language on Right -->
<div class="flex items-center gap-4">
<nav class="nav social flex items-center gap-2">
<?php $social_media = json_decode(setting('social_links'), true); ?>
@if(!empty($social_media['Twitter']))
<a class="!text-[#5daed5] hover:!text-[#3b9ec9] text-[1.1rem] transition-colors" href="{{ $social_media['Twitter'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}" onclick="trackEvent('social_click', {platform: 'twitter'})">
<i class="uil uil-twitter" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Facebook']))
<a class="!text-[#4470cf] hover:!text-[#3259b8] text-[1.1rem] transition-colors" href="{{ $social_media['Facebook'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Facebook') }}" onclick="trackEvent('social_click', {platform: 'facebook'})">
<i class="uil uil-facebook-f" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Github']))
<a class="!text-[#343a40] hover:!text-[#23272b] text-[1.1rem] transition-colors" href="{{ $social_media['Github'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}" onclick="trackEvent('social_click', {platform: 'github'})">
<i class="uil uil-github" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Instagram']))
<a class="!text-[#d53581] hover:!text-[#c12570] text-[1.1rem] transition-colors" href="{{ $social_media['Instagram'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}" onclick="trackEvent('social_click', {platform: 'instagram'})">
<i class="uil uil-instagram" aria-hidden="true"></i>
</a>
@endif
@if(!empty($social_media['Youtube']))
<a class="!text-[#c8312b] hover:!text-[#b02822] text-[1.1rem] transition-colors" href="{{ $social_media['Youtube'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('YouTube') }}" onclick="trackEvent('social_click', {platform: 'youtube'})">
<i class="uil uil-youtube" aria-hidden="true"></i>
</a>
@endif
</nav>
<div class="hidden lg:!block xl:!block shrink-0">
@include("components.custom.language-selector")
</div>
@@ -171,14 +137,7 @@ $isHomepage = $currentRoute === 'homepage' || $currentPath === '/' || request()-
<div>
<a href="mailto:{{setting('contact_email')}}" class="link-inverse">{{setting('contact_email')}}</a>
<br> <a href="tel:{{setting('contact_phone')}}">{{setting('contact_phone')}}</a> <br>
<nav class="nav social social-white !mt-4">
<?php $social_media = json_decode(setting('social_links'), true); ?>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Twitter']) ? $social_media['Twitter'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}"><i class="uil uil-twitter before:content-['\ed59'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Facebook']) ? $social_media['Facebook'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Facebook') }}"><i class="uil uil-facebook-f before:content-['\eae2'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Github']) ? $social_media['Github'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}"><i class="uil uil-github before:content-['\eb40'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Instagram']) ? $social_media['Instagram'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}"><i class="uil uil-instagram before:content-['\eb9c'] !text-white text-[1rem]" aria-hidden="true"></i></a>
<a class="!text-[#cacaca] text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="{{ !empty($social_media['Youtube']) ? $social_media['Youtube'] : '#' }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('YouTube') }}"><i class="uil uil-youtube before:content-['\edb5'] !text-white text-[1rem]" aria-hidden="true"></i></a>
</nav>
</div>
</div>
</div>
+7 -1
View File
@@ -1,8 +1,14 @@
{!! '<' . '?xml version="1.0" encoding="UTF-8"?' . '>' !!}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
@foreach($urls as $url)
<url>
<loc>{{ $url['loc'] }}</loc>
@if(!empty($url['alternates']))
@foreach($url['alternates'] as $alt)
<xhtml:link rel="alternate" hreflang="{{ $alt['hreflang'] }}" href="{{ $alt['href'] }}"/>
@endforeach
@endif
<lastmod>{{ $url['lastmod'] }}</lastmod>
<changefreq>{{ $url['changefreq'] }}</changefreq>
<priority>{{ $url['priority'] }}</priority>
@@ -10,12 +10,27 @@
'EUR' => '€',
'GBP' => '£',
];
$locale = app()->getLocale();
$isTr = ($locale === 'tr');
@endphp
<style>
.bank-accounts-section {
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 40%);
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
}
/* Optimized mobile padding */
.bank-accounts-section .main-container {
padding-top: 2rem;
padding-bottom: 2rem;
}
@media (min-width: 768px) {
.bank-accounts-section .main-container {
padding-top: 4rem;
padding-bottom: 4rem;
}
}
.bank-account-card {
background: #ffffff;
border-radius: 1.25rem;
@@ -171,27 +186,6 @@
.bank-account-card__copy-btn-icon.is-copied {
color: #16a34a;
}
.bank-account-card__swift-copy-btn {
display: inline-flex;
align-items: center;
gap: 0.35rem;
margin-top: 0.5rem;
padding: 0;
border: none;
background: none;
font-size: 0.8125rem;
font-weight: 600;
color: var(--bs-primary);
cursor: pointer;
transition: opacity 0.2s;
}
.bank-account-card__swift-copy-btn:hover {
opacity: 0.8;
text-decoration: underline;
}
.bank-account-card__swift-copy-btn.is-copied {
color: #16a34a;
}
.bank-account-card__footer-bar {
height: 4px;
background: linear-gradient(90deg, var(--bs-primary), #3b82f6);
@@ -202,6 +196,7 @@
.bank-account-card:hover .bank-account-card__footer-bar {
transform: scaleX(1);
}
.bank-accounts-empty {
padding: 3rem 2rem;
background: #ffffff;
@@ -211,35 +206,66 @@
max-width: 420px;
margin: 0 auto;
}
.bank-accounts-tabs {
display: inline-flex;
gap: 0;
border-bottom: 2px solid #e2e8f0;
margin-bottom: 2.5rem;
/* Premium tab styles inspired by why-choose-us */
.nav-tabs.nav-tabs-bg {
display: flex !important;
flex-direction: column !important;
border: none;
background: #f1f5f9;
border-radius: 1rem;
padding: 0.5rem;
gap: 0.5rem;
}
.bank-accounts-tabs a,
.bank-accounts-tabs span {
padding: 0.75rem 1.25rem;
font-size: 0.875rem;
font-weight: 600;
text-decoration: none;
@media (min-width: 768px) {
.nav-tabs.nav-tabs-bg {
flex-direction: row !important;
}
}
.nav-tabs.nav-tabs-bg .nav-item {
margin-bottom: 0 !important;
flex: 1;
}
.nav-tabs.nav-tabs-bg .nav-link {
border: none;
background: transparent;
padding: 1rem 1.25rem;
border-radius: 0.75rem;
display: flex;
align-items: center;
text-align: left;
color: #64748b;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
transition: color 0.2s, border-color 0.2s;
transition: all 0.2s ease;
height: 100%;
}
.bank-accounts-tabs a:hover {
color: #334155;
.nav-tabs.nav-tabs-bg .nav-link:hover {
background: rgba(255, 255, 255, 0.5);
color: #0f172a;
}
.bank-accounts-tabs .is-active {
color: var(--bs-primary);
border-bottom-color: var(--bs-primary);
.nav-tabs.nav-tabs-bg .nav-link.active {
background: #ffffff;
color: #0f172a;
box-shadow: 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.04);
}
.nav-tabs.nav-tabs-bg .nav-link .icon {
display: flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
border-radius: 0.75rem;
background: #f8fafc;
font-size: 1.5rem;
transition: background-color 0.2s;
}
.nav-tabs.nav-tabs-bg .nav-link.active .icon {
background: #f1f5f9;
}
.bank-accounts-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 1.5rem;
margin-bottom: 2.5rem;
}
@media (min-width: 768px) {
.bank-accounts-grid {
@@ -251,13 +277,14 @@
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* Legal info box adaptations */
.legal-info-box {
background: #ffffff;
border-radius: 1.25rem;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 6px -1px rgba(15, 23, 42, 0.06), 0 2px 4px -2px rgba(15, 23, 42, 0.04);
padding: 2.5rem;
margin-top: 3rem;
padding: 2rem;
position: relative;
overflow: hidden;
transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
@@ -352,211 +379,340 @@
.btn-copy-inline.is-copied {
color: #16a34a;
}
/* Elegant Letterhead tab view styles */
.letterhead-tab-container {
background: #f1f5f9;
border-radius: 1.25rem;
padding: 2rem 1rem;
border: 1px solid #e2e8f0;
}
@media (min-width: 768px) {
.letterhead-tab-container {
padding: 3rem 2rem;
}
}
.a4-letterhead-preview-content {
background: #ffffff;
width: 210mm;
height: 297mm;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.02);
border-radius: 8px;
position: relative;
padding: 20mm 15mm;
box-sizing: border-box;
margin: 0 auto;
color: #0f172a !important;
overflow: hidden;
}
.a4-watermark {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 130mm;
height: 130mm;
background-image: url('/logos/truncgil-dikey.svg');
background-repeat: no-repeat;
background-position: center;
background-size: contain;
opacity: 0.15;
pointer-events: none;
z-index: 0;
}
.a4-letterhead-inner {
position: relative;
z-index: 1;
height: 100%;
display: flex;
flex-direction: column;
}
.border-bottom-premium {
border-bottom: 2px solid #e2e8f0;
position: relative;
}
.border-bottom-premium::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 15%;
height: 2px;
background: var(--bs-primary);
}
.bg-light-premium {
background: #f8fafc;
border: 1px solid #e2e8f0;
}
.a4-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
.a4-table th {
background: transparent;
border-bottom: 2px solid #cbd5e1;
padding: 8px 12px;
font-size: 8pt;
font-weight: 700;
text-align: left;
color: #475569;
text-transform: uppercase;
line-height: 1.3;
}
.a4-table td {
padding: 12px;
font-size: 7.5pt;
border-bottom: 1px solid #e2e8f0;
vertical-align: top;
line-height: 1.4;
}
.a4-table tr:last-child td {
border-bottom: 2px solid #cbd5e1;
}
.a4-badge {
display: inline-block;
background: #e0f2fe;
color: #0369a1;
font-weight: 700;
font-size: 7pt;
padding: 2px 6px;
border-radius: 4px;
text-transform: uppercase;
}
@media print {
/* Hide absolutely everything else */
body * {
visibility: hidden !important;
}
html, body {
background: #ffffff !important;
margin: 0 !important;
padding: 0 !important;
width: 210mm !important;
height: 297mm !important;
max-height: 297mm !important;
overflow: hidden !important;
}
#letterheadPrintArea, #letterheadPrintArea * {
visibility: visible !important;
}
#letterheadPrintArea {
position: absolute !important;
left: 0 !important;
top: 0 !important;
width: 210mm !important;
height: 297mm !important;
padding: 20mm 15mm !important;
box-shadow: none !important;
border-radius: 0 !important;
margin: 0 !important;
background: #ffffff !important;
box-sizing: border-box !important;
}
@page {
size: A4 portrait;
margin: 0;
}
}
</style>
<section class="wrapper bank-accounts-section">
<div class="container pt-14 pb-14 xl:pt-20 xl:pb-20 lg:pt-20 lg:pb-20 md:pt-20 md:pb-20">
<div class="container main-container">
<!-- Header -->
<div class="row text-center mb-8">
<div class="col-md-10 col-lg-8 col-xl-7 mx-auto">
<h1 class="display-1 mb-3">{!! $page->title !!}</h1>
@if($page->excerpt)
<p class="lead fs-lg mb-0">{!! $page->excerpt !!}</p>
<h1 class="display-2 mb-3">{!! $page->translate('title') ?: $page->title !!}</h1>
@if($page->translate('excerpt') ?: $page->excerpt)
<p class="lead fs-lg mb-0 text-muted">{!! $page->translate('excerpt') ?: $page->excerpt !!}</p>
@endif
</div>
</div>
<div class="text-center">
<nav class="bank-accounts-tabs" aria-label="{!! t('Ödeme yöntemi') !!}">
<a href="{{ route('page.show', ['slug' => 'online-odeme']) }}">{!! t('Kart ile Ödeme') !!}</a>
<span class="is-active">{!! t('Havale / EFT (IBAN)') !!}</span>
</nav>
<!-- 3-Tab Header Row -->
<div class="row mb-8">
<div class="col-12 col-xl-10 mx-auto">
<ul class="nav nav-tabs nav-tabs-bg flex flex-col md:flex-row justify-between" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" data-bs-toggle="tab" href="#tab-bank-accounts" aria-selected="true" role="tab">
<div class="icon text-primary me-3 flex-shrink-0">
<i class="uil uil-university"></i>
</div>
<div>
<h4 class="mb-0 fs-16 font-bold">{!! $isTr ? t('Banka Hesapları') : t('Bank Accounts') !!}</h4>
<span class="fs-12 text-muted">{!! $isTr ? t('EFT / Havale Bilgileri') : t('EFT / Wire Transfer Details') !!}</span>
</div>
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#tab-legal-info" aria-selected="false" role="tab">
<div class="icon text-success me-3 flex-shrink-0">
<i class="uil uil-shield-check"></i>
</div>
<div>
<h4 class="mb-0 fs-16 font-bold">{!! $isTr ? t('Yasal Bilgiler') : t('Legal Information') !!}</h4>
<span class="fs-12 text-muted">{!! $isTr ? t('Fatura & Sicil Kayıtları') : t('Billing & Registry Records') !!}</span>
</div>
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#tab-letterhead" aria-selected="false" role="tab">
<div class="icon text-warning me-3 flex-shrink-0">
<i class="uil uil-file-check-alt"></i>
</div>
<div>
<h4 class="mb-0 fs-16 font-bold">{!! $isTr ? t('Antetli Kağıt') : t('Letterhead') !!}</h4>
<span class="fs-12 text-muted">{!! $isTr ? t('Resmi Yazı & Hızlı Yazdır') : t('Official Letter & Quick Print') !!}</span>
</div>
</a>
</li>
</ul>
</div>
</div>
<div class="bank-accounts-grid">
@forelse($accounts as $index => $account)
@php
$currency = $account['currency'] ?? 'TRY';
$currencyLabel = ($currencySymbols[$currency] ?? '') . ' ' . $currency;
@endphp
<div>
<article class="bank-account-card">
<div class="bank-account-card__header">
<span class="bank-account-card__currency">{{ $currencyLabel }}</span>
<!-- Tab Content -->
<div class="row">
<div class="col-12 col-xl-10 mx-auto">
<div class="tab-content">
<!-- Tab 1: Bank Accounts -->
<div class="tab-pane fade show active" id="tab-bank-accounts" role="tabpanel">
<div class="bank-accounts-grid">
@forelse($accounts as $index => $account)
@php
$currency = $account['currency'] ?? 'TRY';
$currencyLabel = ($currencySymbols[$currency] ?? '') . ' ' . $currency;
@endphp
<div>
<article class="bank-account-card">
<div class="bank-account-card__header">
<span class="bank-account-card__currency">{{ $currencyLabel }}</span>
@if(!empty($account['logo']))
<div class="bank-account-card__logo-wrap">
<img
src="{{ asset('storage/' . $account['logo']) }}"
alt="{{ $account['bank_name'] }}"
loading="lazy"
>
@if(!empty($account['logo']))
<div class="bank-account-card__logo-wrap">
<img
src="{{ asset('storage/' . $account['logo']) }}"
alt="{{ $account['bank_name'] }}"
loading="lazy"
>
</div>
@else
<div class="bank-account-card__logo-placeholder" aria-hidden="true">
<i class="uil uil-university"></i>
</div>
@endif
</div>
<div class="bank-account-card__body">
<h2 class="bank-account-card__title">{{ $account['bank_name'] }}</h2>
@if(!empty($account['branch_name']))
<p class="bank-account-card__branch">{{ $account['branch_name'] }}</p>
@endif
<div class="bank-account-card__field">
<span class="bank-account-card__label">{!! $isTr ? t('Hesap Sahibi') : t('Account Holder') !!}</span>
<div class="bank-account-card__value">{{ $account['account_holder'] }}</div>
</div>
<div class="bank-account-card__iban-box">
<span class="bank-account-card__label">IBAN</span>
<div style="position: relative;">
<div class="bank-account-card__iban" id="iban-{{ $index }}" style="padding-right: 3rem;">{{ $account['iban'] }}</div>
<button
type="button"
class="bank-account-card__copy-btn-icon"
data-iban="{{ $account['iban'] }}"
data-copy-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}"
data-copied-label="{!! $isTr ? t('Kopyalandı') : t('Copied') !!}"
aria-label="{!! $isTr ? t('IBAN kopyala') : t('Copy IBAN') !!}"
>
<i class="uil uil-copy" style="font-size: 1.15rem;"></i>
</button>
</div>
</div>
@php
$swiftCode = !empty($account['swift_code']) ? $account['swift_code'] : 'TRHBTR2A';
@endphp
@if($currency !== 'TRY' || !empty($account['swift_code']))
<div class="bank-account-card__swift-box mt-3">
<span class="bank-account-card__label">SWIFT / BIC</span>
<div style="position: relative;">
<div class="bank-account-card__iban" id="swift-{{ $index }}" style="padding-right: 3rem;">{{ $swiftCode }}</div>
<button
type="button"
class="bank-account-card__copy-btn-icon"
data-iban="{{ $swiftCode }}"
data-copy-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}"
data-copied-label="{!! $isTr ? t('Kopyalandı') : t('Copied') !!}"
aria-label="{!! $isTr ? t('SWIFT kopyala') : t('Copy SWIFT') !!}"
>
<i class="uil uil-copy" style="font-size: 1.15rem;"></i>
</button>
</div>
</div>
@endif
</div>
<div class="bank-account-card__footer-bar" aria-hidden="true"></div>
</article>
</div>
@else
<div class="bank-account-card__logo-placeholder" aria-hidden="true">
<i class="uil uil-university"></i>
</div>
@endif
</div>
<div class="bank-account-card__body">
<h2 class="bank-account-card__title">{{ $account['bank_name'] }}</h2>
@if(!empty($account['branch_name']))
<p class="bank-account-card__branch">{{ $account['branch_name'] }}</p>
@endif
<div class="bank-account-card__field">
<span class="bank-account-card__label">{!! t('Hesap Sahibi') !!}</span>
<div class="bank-account-card__value">{{ $account['account_holder'] }}</div>
</div>
<div class="bank-account-card__iban-box">
<span class="bank-account-card__label">IBAN</span>
<div style="position: relative;">
<div class="bank-account-card__iban" id="iban-{{ $index }}" style="padding-right: 3rem;">{{ $account['iban'] }}</div>
<button
type="button"
class="bank-account-card__copy-btn-icon"
data-iban="{{ $account['iban'] }}"
data-copy-label="{!! t('Kopyala') !!}"
data-copied-label="{!! t('Kopyalandı') !!}"
aria-label="{!! t('IBAN kopyala') !!}"
>
<i class="uil uil-copy" style="font-size: 1.15rem;"></i>
</button>
</div>
</div>
@php
$swiftCode = !empty($account['swift_code']) ? $account['swift_code'] : 'TRHBTR2A';
@endphp
@if($currency !== 'TRY' || !empty($account['swift_code']))
<div class="bank-account-card__swift-box mt-3">
<span class="bank-account-card__label">SWIFT / BIC</span>
<div style="position: relative;">
<div class="bank-account-card__iban" id="swift-{{ $index }}" style="padding-right: 3rem;">{{ $swiftCode }}</div>
<button
type="button"
class="bank-account-card__copy-btn-icon"
data-iban="{{ $swiftCode }}"
data-copy-label="{!! t('Kopyala') !!}"
data-copied-label="{!! t('Kopyalandı') !!}"
aria-label="{!! t('SWIFT kopyala') !!}"
>
<i class="uil uil-copy" style="font-size: 1.15rem;"></i>
</button>
@empty
<div class="col-12">
<div class="bank-accounts-empty">
<i class="uil uil-wallet text-6xl text-slate-300 mb-4 d-block"></i>
<h3 class="h4 text-slate-500 mb-2">{!! $isTr ? t('Henüz banka hesabı eklenmemiş.') : t('No bank accounts have been added yet.') !!}</h3>
<p class="text-slate-400 mb-0">{!! $isTr ? t('Yönetim panelinden banka hesaplarınızı ekleyebilirsiniz.') : t('You can add your bank accounts from the administration panel.') !!}</p>
</div>
</div>
@endif
@endforelse
</div>
</div>
<div class="bank-account-card__footer-bar" aria-hidden="true"></div>
</article>
</div>
@empty
<div class="col-12">
<div class="bank-accounts-empty">
<i class="uil uil-wallet text-6xl text-slate-300 mb-4 d-block"></i>
<h3 class="h4 text-slate-500 mb-2">{!! t('Henüz banka hesabı eklenmemiş.') !!}</h3>
<p class="text-slate-400 mb-0">{!! t('Yönetim panelinden banka hesaplarınızı ekleyebilirsiniz.') !!}</p>
<!-- Tab 2: Legal Information -->
<div class="tab-pane fade" id="tab-legal-info" role="tabpanel">
@include('templates.corporate.partials.bank-accounts-legal-info')
</div>
</div>
@endforelse
</div>
<!-- Legal Info Box -->
<div class="legal-info-box">
<div class="legal-info-box__decor" aria-hidden="true">
<i class="uil uil-shield-check"></i>
</div>
<div class="legal-info-box__content">
<h3 class="legal-info-box__title">
<i class="uil uil-info-circle"></i>
<span>{!! t('Yasal & Fatura Bilgileri') !!}</span>
</h3>
<div class="legal-info-grid">
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('Firma Ünvanı') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ</span>
<button type="button" class="btn-copy-inline" data-copy-target="TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
<!-- Tab 3: Official Letterhead Preview & Print -->
<div class="tab-pane fade" id="tab-letterhead" role="tabpanel">
<div class="text-center mb-6 d-flex justify-content-center gap-3 flex-wrap">
<button type="button" class="btn btn-outline-primary rounded-pill px-4 shadow-sm" onclick="window.print()">
<i class="uil uil-print me-2" style="font-size: 1.2rem; vertical-align: middle;"></i>
<strong>{!! $isTr ? t('Yazdır') : t('Print') !!}</strong>
</button>
<button type="button" class="btn btn-primary rounded-pill px-4 shadow-sm text-white" style="background-color: #e31e24 !important; border-color: #e31e24 !important; color: #ffffff !important;" onclick="downloadPDF(this)">
<i class="uil uil-file-download-alt me-2" style="font-size: 1.2rem; vertical-align: middle;"></i>
<strong>{!! $isTr ? t('PDF İndir') : t('Download PDF') !!}</strong>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('Vergi Dairesi') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>Şahinbey V.D.</span>
<button type="button" class="btn-copy-inline" data-copy-target="Şahinbey V.D." aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('Vergi Numarası') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>8591492435</span>
<button type="button" class="btn-copy-inline" data-copy-target="8591492435" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('MERSİS No') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>0859149243500001</span>
<button type="button" class="btn-copy-inline" data-copy-target="0859149243500001" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('D-U-N-S No') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>595948152</span>
<button type="button" class="btn-copy-inline" data-copy-target="595948152" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('SGK Sicil No') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>2-6210-0101-1219355-027-08-80-000</span>
<button type="button" class="btn-copy-inline" data-copy-target="2-6210-0101-1219355-027-08-80-000" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('E-Posta') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<a href="mailto:info@truncgil.com.tr" class="text-reset" style="text-decoration: none;">info@truncgil.com.tr</a>
<button type="button" class="btn-copy-inline" data-copy-target="info@truncgil.com.tr" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('Adres') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A İç Kapı No: 1 Şahinbey / Gaziantep</span>
<button type="button" class="btn-copy-inline" data-copy-target="Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A İç Kapı No: 1 Şahinbey / Gaziantep" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
<div class="letterhead-tab-container">
<div class="table-responsive" style="overflow-x: auto; width: 100%;">
<div style="min-width: 215mm; padding: 10px 0;">
@include('templates.corporate.partials.bank-accounts-letterhead')
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@if($page->content)
<div class="row mt-14">
<!-- Dynamic Content from DB -->
@php
$pageContent = $page->translate('content') ?: $page->content;
@endphp
@if($pageContent)
<div class="row mt-12">
<div class="col-lg-10 mx-auto">
<div class="prose max-w-none text-center text-gray-600">
{!! $page->content !!}
{!! $pageContent !!}
</div>
</div>
</div>
@@ -593,37 +749,6 @@ document.querySelectorAll('.bank-account-card__copy-btn-icon').forEach(function
});
});
document.querySelectorAll('.bank-account-card__swift-copy-btn').forEach(function (btn) {
btn.addEventListener('click', function () {
var swift = this.getAttribute('data-swift');
var copyLabel = this.getAttribute('data-copy-label');
var copiedLabel = this.getAttribute('data-copied-label');
var span = this.querySelector('span');
var icon = this.querySelector('i');
function showCopied() {
btn.classList.add('is-copied');
if (span) span.textContent = copiedLabel;
if (icon) icon.className = 'uil uil-check';
setTimeout(function () {
btn.classList.remove('is-copied');
if (span) span.textContent = copyLabel;
if (icon) icon.className = 'uil uil-copy';
}, 2000);
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(swift).then(showCopied).catch(function () {
fallbackCopy(swift);
showCopied();
});
} else {
fallbackCopy(swift);
showCopied();
}
});
});
function fallbackCopy(text) {
var ta = document.createElement('textarea');
ta.value = text;
@@ -661,6 +786,63 @@ document.querySelectorAll('.btn-copy-inline').forEach(function (btn) {
}
});
});
function downloadPDF(btn) {
var originalHTML = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" style="width: 1rem; height: 1rem; vertical-align: middle;"></span> <strong>' + (document.documentElement.lang === 'tr' ? 'Lütfen bekleyin...' : 'Please wait...') + '</strong>';
function run() {
var element = document.getElementById('letterheadPrintArea');
// Save original styles
var originalShadow = element.style.boxShadow;
var originalHeight = element.style.height;
// Apply temporary safe styles for PDF capture to fit exactly in 1 page (A4 is 297mm)
element.style.boxShadow = 'none';
element.style.height = '296mm';
var opt = {
margin: 0,
filename: 'truncgil-bank-accounts.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true, letterRendering: true, scrollY: 0 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
html2pdf().set(opt).from(element).save().then(function() {
// Restore styles
element.style.boxShadow = originalShadow;
element.style.height = originalHeight;
btn.disabled = false;
btn.innerHTML = originalHTML;
}).catch(function(err) {
console.error(err);
// Restore styles
element.style.boxShadow = originalShadow;
element.style.height = originalHeight;
btn.disabled = false;
btn.innerHTML = originalHTML;
});
}
if (typeof html2pdf === 'undefined') {
var script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js';
script.onload = run;
script.onerror = function() {
alert('PDF library could not be loaded. Please try again.');
btn.disabled = false;
btn.innerHTML = originalHTML;
};
document.head.appendChild(script);
} else {
run();
}
}
</script>
@endpush
@@ -0,0 +1,428 @@
@extends('layouts.site')
@section('content')
<style>
.imprint-section {
background: linear-gradient(180deg, #f8fafc 0%, #ffffff 40%);
}
.imprint-card {
background: #ffffff;
border-radius: 1.25rem;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 6px -1px rgba(15, 23, 42, 0.06), 0 2px 4px -2px rgba(15, 23, 42, 0.04);
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.imprint-card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 25px -5px rgba(15, 23, 42, 0.1), 0 8px 10px -6px rgba(15, 23, 42, 0.06);
border-color: #cbd5e1;
}
.imprint-card__header {
position: relative;
padding: 2rem 1.5rem 1.5rem;
border-bottom: 1px solid #e2e8f0;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #f8fafc;
}
.imprint-card__icon-wrap {
width: 64px;
height: 64px;
border-radius: 1rem;
background: rgba(var(--bs-primary-rgb), 0.1);
color: var(--bs-primary);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.75rem;
margin-bottom: 0.75rem;
}
.imprint-card__body {
padding: 1.5rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.imprint-card__title {
font-size: 1.25rem;
font-weight: 700;
color: #0f172a;
margin-bottom: 0;
line-height: 1.3;
}
.imprint-card__field {
display: flex;
flex-direction: column;
border-bottom: 1px dashed #e2e8f0;
padding-bottom: 0.75rem;
}
.imprint-card__field:last-of-type {
border-bottom: none;
padding-bottom: 0;
}
.imprint-card__label {
display: block;
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #94a3b8;
margin-bottom: 0.35rem;
}
.imprint-card__value-container {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.imprint-card__value {
font-size: 0.9375rem;
font-weight: 600;
color: #1e293b;
line-height: 1.4;
word-break: break-word;
}
.imprint-card__footer-bar {
height: 4px;
background: linear-gradient(90deg, var(--bs-primary), #3b82f6);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.4s ease;
}
.imprint-card:hover .imprint-card__footer-bar {
transform: scaleX(1);
}
.imprint-tabs {
display: inline-flex;
gap: 0;
border-bottom: 2px solid #e2e8f0;
margin-bottom: 2.5rem;
}
.imprint-tabs a,
.imprint-tabs span {
padding: 0.75rem 1.25rem;
font-size: 0.875rem;
font-weight: 600;
text-decoration: none;
color: #64748b;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
transition: color 0.2s, border-color 0.2s;
}
.imprint-tabs a:hover {
color: #334155;
}
.imprint-tabs .is-active {
color: var(--bs-primary);
border-bottom-color: var(--bs-primary);
}
.imprint-grid {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 1.5rem;
margin-bottom: 2.5rem;
}
@media (min-width: 768px) {
.imprint-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.imprint-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.btn-copy-inline {
display: inline-flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
padding: 4px;
color: #94a3b8;
cursor: pointer;
font-size: 0.875rem;
line-height: 1;
border-radius: 4px;
flex-shrink: 0;
transition: color 0.2s, background-color 0.2s, transform 0.1s;
}
.btn-copy-inline:hover {
color: var(--bs-primary);
background-color: #f1f5f9;
}
.btn-copy-inline:active {
transform: scale(0.9);
}
.btn-copy-inline.is-copied {
color: #16a34a;
}
</style>
<section class="wrapper imprint-section">
<div class="container pt-14 pb-14 xl:pt-20 xl:pb-20 lg:pt-20 lg:pb-20 md:pt-20 md:pb-20">
<div class="row text-center mb-8">
<div class="col-md-10 col-lg-8 col-xl-7 mx-auto">
<h1 class="display-1 mb-3">{!! $page->translate('title') ?: t('Künye') !!}</h1>
<p class="lead fs-lg mb-0">{!! $page->translate('excerpt') ?: t('Resmi şirket bilgileri ve yasal sorumluluklar.') !!}</p>
</div>
</div>
<div class="text-center">
<nav class="imprint-tabs" aria-label="{!! t('Kurumsal Bilgiler') !!}">
<a href="{{ route('page.show', ['slug' => 'banka-bilgilerimiz']) }}">{!! t('Havale / EFT (IBAN)') !!}</a>
<span class="is-active">{!! t('Künye') !!}</span>
</nav>
</div>
<div class="imprint-grid">
<!-- Card 1: Kurumsal Bilgiler -->
<div>
<article class="imprint-card">
<div class="imprint-card__header">
<div class="imprint-card__icon-wrap">
<i class="uil uil-building"></i>
</div>
<h2 class="imprint-card__title">{!! t('Kurumsal Bilgiler') !!}</h2>
</div>
<div class="imprint-card__body">
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Firma Ünvanı') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">{!! t('TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Yetkili Temsilci') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">Mehmet Ali Trunç</span>
<button type="button" class="btn-copy-inline" data-copy-target="Mehmet Ali Trunç" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Sorumlu Müdür') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">Mehmet Ali Trunç</span>
<button type="button" class="btn-copy-inline" data-copy-target="Mehmet Ali Trunç" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Yer Sağlayıcı') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">{!! t('TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
</div>
<div class="imprint-card__footer-bar" aria-hidden="true"></div>
</article>
</div>
<!-- Card 2: Resmi Bilgiler -->
<div>
<article class="imprint-card">
<div class="imprint-card__header">
<div class="imprint-card__icon-wrap">
<i class="uil uil-file-shield-alt"></i>
</div>
<h2 class="imprint-card__title">{!! t('Yasal Künye Bilgileri') !!}</h2>
</div>
<div class="imprint-card__body">
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Vergi Dairesi') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">{!! t('Şahinbey V.D.') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="Şahinbey V.D." aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Vergi Numarası') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">8591492435</span>
<button type="button" class="btn-copy-inline" data-copy-target="8591492435" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('MERSİS No') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">0859149243500001</span>
<button type="button" class="btn-copy-inline" data-copy-target="0859149243500001" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Ticaret Sicil No') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">49454</span>
<button type="button" class="btn-copy-inline" data-copy-target="49454" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Ticaret Odası') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">{!! t('Gaziantep Ticaret Odası (GTO)') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="Gaziantep Ticaret Odası (GTO)" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
</div>
<div class="imprint-card__footer-bar" aria-hidden="true"></div>
</article>
</div>
<!-- Card 3: İletişim Bilgileri -->
<div>
<article class="imprint-card">
<div class="imprint-card__header">
<div class="imprint-card__icon-wrap">
<i class="uil uil-envelope-shield"></i>
</div>
<h2 class="imprint-card__title">{!! t('İletişim ve Resmi Bilgiler') !!}</h2>
</div>
<div class="imprint-card__body">
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Telefon') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">+90 850 305 45 45</span>
<button type="button" class="btn-copy-inline" data-copy-target="+90 850 305 45 45" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('E-Posta') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value"><a href="mailto:info@truncgil.com.tr" class="text-reset" style="text-decoration: none;">info@truncgil.com.tr</a></span>
<button type="button" class="btn-copy-inline" data-copy-target="info@truncgil.com.tr" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('KEP Adresi') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">truncgilteknoloji@hs01.kep.tr</span>
<button type="button" class="btn-copy-inline" data-copy-target="truncgilteknoloji@hs01.kep.tr" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="imprint-card__field">
<span class="imprint-card__label">{!! t('Adres') !!}</span>
<div class="imprint-card__value-container">
<span class="imprint-card__value">{!! t('Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A İç Kapı No: 1 Şahinbey / Gaziantep') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A İç Kapı No: 1 Şahinbey / Gaziantep" aria-label="{!! t('Kopyala') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
</div>
<div class="imprint-card__footer-bar" aria-hidden="true"></div>
</article>
</div>
</div>
@php
$pageContent = $page ? ($page->translate('content') ?: $page->content) : null;
@endphp
@if($pageContent)
<div class="row mt-14">
<div class="col-lg-10 mx-auto">
<div class="prose max-w-none text-center text-gray-600">
{!! $pageContent !!}
</div>
</div>
</div>
@endif
</div>
</section>
@push('scripts')
<script>
document.querySelectorAll('.btn-copy-inline').forEach(function (btn) {
btn.addEventListener('click', function () {
var text = this.getAttribute('data-copy-target');
var icon = this.querySelector('i');
function showCopied() {
btn.classList.add('is-copied');
if (icon) icon.className = 'uil uil-check text-success';
setTimeout(function () {
btn.classList.remove('is-copied');
if (icon) icon.className = 'uil uil-copy';
}, 1500);
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(showCopied).catch(function () {
fallbackCopy(text);
showCopied();
});
} else {
fallbackCopy(text);
showCopied();
}
});
});
function fallbackCopy(text) {
var ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.position = 'absolute';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
</script>
@endpush
@endsection
@@ -0,0 +1,91 @@
@php
$locale = app()->getLocale();
$isTr = ($locale === 'tr');
@endphp
<!-- Legal Info Box -->
<div class="legal-info-box">
<div class="legal-info-box__decor" aria-hidden="true">
<i class="uil uil-shield-check"></i>
</div>
<div class="legal-info-box__content">
<h3 class="legal-info-box__title">
<i class="uil uil-info-circle"></i>
<span>{!! $isTr ? t('Yasal & Fatura Bilgileri') : t('Legal & Billing Information') !!}</span>
</h3>
<div class="legal-info-grid">
<div class="legal-info-item">
<span class="legal-info-item__label">{!! $isTr ? t('Firma Ünvanı') : t('Company Name') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>{!! t('TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="TRUNÇGİL TEKNOLOJİ SANAYİ VE TİCARET LİMİTED ŞİRKETİ" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! $isTr ? t('Vergi Dairesi') : t('Tax Office') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>{!! $isTr ? t('Şahinbey V.D.') : t('Sahinbey Tax Office') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="{{ $isTr ? 'Şahinbey V.D.' : 'Sahinbey Tax Office' }}" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! $isTr ? t('Vergi Numarası') : t('Tax ID') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>8591492435</span>
<button type="button" class="btn-copy-inline" data-copy-target="8591492435" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('MERSİS No') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>0859149243500001</span>
<button type="button" class="btn-copy-inline" data-copy-target="0859149243500001" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! t('D-U-N-S No') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>595948152</span>
<button type="button" class="btn-copy-inline" data-copy-target="595948152" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! $isTr ? t('SGK Sicil No') : t('Social Security Register No') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>2-6210-0101-1219355-027-08-80-000</span>
<button type="button" class="btn-copy-inline" data-copy-target="2-6210-0101-1219355-027-08-80-000" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! $isTr ? t('E-Posta') : t('Email') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<a href="mailto:info@truncgil.com.tr" class="text-reset" style="text-decoration: none;">info@truncgil.com.tr</a>
<button type="button" class="btn-copy-inline" data-copy-target="info@truncgil.com.tr" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
<div class="legal-info-item">
<span class="legal-info-item__label">{!! $isTr ? t('Adres') : t('Address') !!}</span>
<div class="legal-info-item__value d-flex align-items-center justify-content-between gap-2">
<span>{!! $isTr ? t('Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A İç Kapı No: 1 Şahinbey / Gaziantep') : t('Camtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A Interior Door No: 1 Sahinbey / Gaziantep / TURKEY') !!}</span>
<button type="button" class="btn-copy-inline" data-copy-target="{{ $isTr ? 'Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A İç Kapı No: 1 Şahinbey / Gaziantep' : 'Camtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark No: 4A Interior Door No: 1 Sahinbey / Gaziantep / TURKEY' }}" aria-label="{!! $isTr ? t('Kopyala') : t('Copy') !!}">
<i class="uil uil-copy"></i>
</button>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,178 @@
@php
$locale = app()->getLocale();
$isTr = ($locale === 'tr');
@endphp
<!-- The actual A4 letterhead container -->
<div class="a4-letterhead-print a4-letterhead-preview-content shadow-2xl" id="letterheadPrintArea">
<!-- Center faded Watermark -->
<div class="a4-watermark"></div>
<!-- Content Area -->
<div class="a4-letterhead-inner">
<!-- Header Row -->
<div class="d-flex justify-content-between align-items-start pb-3 mb-4 border-bottom-premium">
<div class="a4-logo-wrap" style="margin-top: 0; padding-top: 0; line-height: 1;">
<img src="{{ asset('logos/truncgil-dikey.svg') }}" alt="Trunçgil Teknoloji" style="height: 28mm; max-height: 28mm; width: auto; object-fit: contain; display: block; margin-top: 0;">
</div>
<div class="text-end a4-company-info" style="font-size: 8pt; line-height: 1.4; color: #334155; font-weight: 500; margin-top: -25mm;">
<h5 style="font-size: 10pt; font-weight: 800; color: #0f172a; margin: 0 0 4px 0; padding: 0; line-height: 1.1;">TRUNÇGİL TEKNOLOJİ SAN. VE TİC. LTD. ŞTİ.</h5>
<div>Çamtepe Mah. Mahmut Tevfik Atay Bul. Gaziantep Teknopark</div>
<div>No: 4A İç Kapı No: 1 Şahinbey / Gaziantep / TÜRKİYE</div>
@if($isTr)
<div class="mt-1"><strong>Vergi Dairesi:</strong> Şahinbey V.D. &nbsp;|&nbsp; <strong>Vergi No:</strong> 8591492435</div>
<div><strong>MERSİS No:</strong> 0859149243500001 &nbsp;|&nbsp; <strong>D-U-N-S No:</strong> 595948152</div>
<div><strong>E-Posta:</strong> info@truncgil.com.tr &nbsp;|&nbsp; <strong>Web:</strong> www.truncgil.com.tr</div>
@else
<div class="mt-1"><strong>Tax Office:</strong> Şahinbey V.D. &nbsp;|&nbsp; <strong>Tax ID:</strong> 8591492435</div>
<div><strong>MERSİS No:</strong> 0859149243500001 &nbsp;|&nbsp; <strong>D-U-N-S No:</strong> 595948152</div>
<div><strong>Email:</strong> info@truncgil.com.tr &nbsp;|&nbsp; <strong>Web:</strong> www.truncgil.com.tr</div>
@endif
</div>
</div>
<!-- Title and Date -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="a4-doc-title mb-1" style="font-size: 13pt; font-weight: 800; letter-spacing: 0.05em; color: #0f172a; margin: 0;">
@if($isTr)
BANKA HESAP BİLGİLERİ
@else
BANK ACCOUNT DETAILS
@endif
</h4>
<p class="text-muted mb-0" style="font-size: 7.5pt; font-style: italic;">
@if($isTr)
Resmi Tedarikçi Kayıt Bildirimi
@else
Official Vendor Registration Bank Declaration
@endif
</p>
</div>
<div class="text-end" style="font-size: 8pt; font-weight: 600; color: #475569;">
@if($isTr)
<div><strong>Tarih:</strong> {{ date('d.m.Y') }}</div>
<div><strong>Konu:</strong> Banka Hesap Bildirimi</div>
@else
<div><strong>Date:</strong> {{ date('d.m.Y') }}</div>
<div><strong>Subject:</strong> Bank Account Declaration</div>
@endif
</div>
</div>
<!-- Description Statement -->
<div class="p-3 rounded mb-4" style="border-left: 3.5px solid var(--bs-primary); font-size: 8pt; line-height: 1.5; color: #334155; font-weight: 500;">
@if($isTr)
<p class="mb-0"><strong>Sayın Yetkili,</strong> Şirketimizin resmi banka hesap bilgileri aşağıda bilgilerinize sunulmuştur. Tedarikçi kayıt süreçlerinde ve tüm ticari işlemlerimizde bu hesapların kullanılmasını rica ederiz.</p>
@else
<p class="mb-0"><strong>Dear Sir/Madam,</strong> The official and active bank account details of our company are presented below for your registration and commercial processes.</p>
@endif
</div>
<!-- Bank Accounts Table -->
<table class="a4-table mb-5">
<thead>
<tr>
<th style="width: 30%;">
@if($isTr)
Banka Adı
@else
Bank Name
@endif
</th>
<th style="width: 30%;">
@if($isTr)
Hesap Sahibi
@else
Account Holder
@endif
</th>
<th style="width: 10%;">
@if($isTr)
Para Birimi
@else
Currency
@endif
</th>
<th style="width: 30%;">IBAN / SWIFT / BIC</th>
</tr>
</thead>
<tbody>
@foreach($accounts as $index => $account)
@php
$currency = $account['currency'] ?? 'TRY';
$currencyLabel = ($currencySymbols[$currency] ?? '') . ' ' . $currency;
$swiftCode = !empty($account['swift_code']) ? $account['swift_code'] : 'TRHBTR2A';
@endphp
<tr>
<td class="font-bold" style="color: #0f172a;">
<strong>{{ $account['bank_name'] }}</strong>
@if(!empty($account['branch_name']))
<div style="font-size: 7pt; color: #64748b; font-weight: 500; margin-top: 2px;">{{ $account['branch_name'] }}</div>
@endif
</td>
<td style="font-size: 7.5pt; font-weight: 600; color: #334155;">{{ $account['account_holder'] }}</td>
<td><span class="a4-badge">{{ $currency }}</span></td>
<td>
<div style="font-family: monospace; font-size: 8pt; font-weight: 700; letter-spacing: 0.02em; color: var(--bs-primary);">
{{ $account['iban'] }}
</div>
<div style="font-size: 7pt; color: #64748b; margin-top: 4px; font-weight: 500;">
<strong>SWIFT / BIC:</strong> <span style="font-family: monospace; font-weight: 700; color: #334155;">{{ $swiftCode }}</span>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
<!-- Sign & Stamp section -->
<div class="row mt-auto pt-3">
<div class="col-6">
<div style="font-size: 7.5pt; line-height: 1.5; color: #64748b;">
<strong>
@if($isTr)
İletişim:
@else
Contact:
@endif
</strong><br>
Trunçgil Site Office: Gaziantep Teknopark<br>
Tel: +90 (551) 234 36 95<br>
Web: www.truncgil.com.tr
</div>
</div>
<div class="col-6 text-end">
<div style="display: inline-block; text-align: center; border-top: 1.5px solid #cbd5e1; padding-top: 8px; width: 65mm;">
<h6 class="mb-1" style="font-size: 8.5pt; font-weight: 800; color: #0f172a; margin: 0;">TRUNÇGİL TEKNOLOJİ SAN. VE TİC. LTD. ŞTİ.</h6>
<span style="font-size: 7pt; color: #64748b; font-weight: 600; text-transform: uppercase;">
@if($isTr)
Yetkili İmza & Kaşe
@else
Authorized Signature & Stamp
@endif
</span>
<div style="height: 20mm; position: relative;" class="stamp-signature-space">
<!-- A subtle premium hint for stamp placement -->
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border: 1.5px dashed #cbd5e1; border-radius: 50%; width: 16mm; height: 16mm; display: flex; align-items: center; justify-content: center; font-size: 6pt; color: #94a3b8; pointer-events: none;">
Stamp
</div>
</div>
</div>
</div>
</div>
<!-- Absolute footer page number / disclaimer -->
<div style="position: absolute; bottom: 15mm; left: 15mm; right: 15mm; font-size: 6.5pt; color: #94a3b8; border-top: 1px solid #e2e8f0; padding-top: 8px;" class="d-flex justify-content-between align-items-center">
<span>
@if($isTr)
* Bu belge elektronik olarak üretilmiştir.
@else
* This document has been generated electronically.
@endif
</span>
<span>Page 1 of 1</span>
</div>
</div>
</div>
+60 -3
View File
@@ -1,4 +1,7 @@
<section class="wrapper image-wrapper bg-image bg-overlay bg-overlay-300 !bg-fixed bg-no-repeat bg-[center_center] bg-cover relative z-0 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,.3)]" data-image-src="./assets/img/photos/bg16.webp" style="background-image: url('./assets/img/photos/bg16.webp');">
@php
$hero_bg = setting('hero_bg') ?: './assets/img/photos/bg16.webp';
@endphp
<section class="wrapper image-wrapper bg-image bg-overlay bg-overlay-300 bg-no-repeat bg-[center_center] bg-cover relative z-0 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,.3)]" data-image-src="{{ $hero_bg }}" style="background-image: url('{{ $hero_bg }}');">
<div class="container pt-28 pb-36 xl:pt-32 lg:pt-32 md:pt-32 xl:pb-28 lg:pb-28 md:pb-28 !text-center">
<div class="flex flex-wrap mx-[-15px]">
<div class="lg:w-8/12 xl:w-7/12 xxl:w-6/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto" data-cues="slideInDown" data-group="page-title" data-disabled="true">
@@ -7,8 +10,39 @@
</h1>
<p class="lead !text-[1.2rem] !text-white !leading-[1.5] font-medium !mb-7 md:mx-[4rem] lg:mx-[2.5rem] xl:mx-[2.5rem]" data-cue="slideInDown" data-group="page-title" data-show="true" style="animation-name: slideInDown; animation-duration: 700ms; animation-timing-function: ease; animation-delay: 300ms; animation-direction: normal; animation-fill-mode: both;">{{ t('Hayatı kolaylaştıran uygulamaları insan odaklı, akılcı ve sade bir biçimde gerçekleştirmek için var gücümüzle çalışıyoruz.') }}</p>
<div data-cue="slideInDown" data-group="page-title" data-show="true" style="animation-name: slideInDown; animation-duration: 700ms; animation-timing-function: ease; animation-delay: 600ms; animation-direction: normal; animation-fill-mode: both;">
<a href="{{ route('page.show', ['slug' => 'neler-yapariz']) }}" class="btn btn-white !rounded-[50rem] !mb-10 xxl:!mb-5">{{ t('Daha Fazla Oku') }}</a>
<a href="{{ route('page.show', ['slug' => 'online-odeme']) }}" class="btn btn-white !rounded-[50rem] !mb-10 xxl:!mb-5 !ml-2 md:!ml-4">{{ t('Online Ödeme') }}</a>
@php $social_media = json_decode(setting('social_links'), true) ?? []; @endphp
<nav class="nav social social-white justify-center !text-center" aria-label="{{ t('Sosyal medya') }}">
@if(!empty($social_media['Twitter']))
<a class="!text-white hover:!text-[#5daed5] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $social_media['Twitter'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Twitter') }}"><i class="uil uil-twitter before:content-['\ed59']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@if(!empty($social_media['Instagram']))
<a class="!text-white hover:!text-[#c13584] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $social_media['Instagram'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Instagram') }}"><i class="uil uil-instagram before:content-['\eb9c']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@if(!empty($social_media['Youtube']))
<a class="!text-white hover:!text-[#ff0000] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $social_media['Youtube'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('YouTube') }}"><i class="uil uil-youtube before:content-['\edb5']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@if(!empty($social_media['Github']))
<a class="!text-white hover:!text-[#2dba4e] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $social_media['Github'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('GitHub') }}"><i class="uil uil-github before:content-['\eb40']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@if(!empty($social_media['Linkedin']))
<a class="!text-white hover:!text-[#0077b5] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $social_media['Linkedin'] }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('LinkedIn') }}"><i class="uil uil-linkedin before:content-['\ebd5']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@php
$google_play = $social_media['googleplay'] ?? $social_media['google_play'] ?? $social_media['GooglePlay'] ?? $social_media['Googleplay'] ?? '';
$apple_store = $social_media['applestore'] ?? $social_media['apple_store'] ?? $social_media['AppleStore'] ?? $social_media['Applestore'] ?? $social_media['apple'] ?? $social_media['Apple'] ?? '';
$microsoft_store = $social_media['microsoftstore'] ?? $social_media['microsoft_store'] ?? $social_media['MicrosoftStore'] ?? $social_media['Microsoftstore'] ?? 'https://apps.microsoft.com/search/publisher?name=Truncgil+Teknoloji&hl=tr-TR&gl=TR';
@endphp
@if(!empty($google_play))
<a class="!text-white hover:!text-[#34A853] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $google_play }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Google Play') }}"><i class="uil uil-google-play before:content-['\eb4f']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@if(!empty($apple_store))
<a class="!text-white hover:!text-[#A2AAAD] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $apple_store }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('App Store') }}"><i class="uil uil-apple before:content-['\e938']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
@if(!empty($microsoft_store))
<a class="!text-white hover:!text-[#00A4EF] transition-all duration-[0.2s] ease-in-out translate-y-0 hover:translate-y-[-0.15rem] mx-3" style="font-size: 2.25rem !important;" href="{{ $microsoft_store }}" target="_blank" rel="noopener noreferrer" aria-label="{{ t('Microsoft Store') }}"><i class="uil uil-microsoft before:content-['\ec03']" style="font-size: 2.25rem !important;" aria-hidden="true"></i></a>
@endif
</nav>
</div>
</div>
<!-- /column -->
@@ -44,4 +78,27 @@
left: 50%;
transform: translateX(-50%) !important;
}
@media (max-width: 767.98px) {
.image-wrapper {
background-image: none !important;
position: relative !important;
}
.image-wrapper::before {
content: "" !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100vh !important;
background-image: linear-gradient(rgba(30, 34, 40, 0.45), rgba(30, 34, 40, 0.45)), url('{{ $hero_bg }}') !important;
background-size: cover !important;
background-position: center center !important;
background-repeat: no-repeat !important;
z-index: -1 !important;
}
.image-wrapper .divider {
display: none !important;
}
}
</style>
@@ -1,55 +1,51 @@
<section class="wrapper !bg-[#ffffff]">
<div class="container">
<div class="flex flex-wrap mx-[-15px] xl:mx-[-12.5px] lg:mx-[-12.5px] md:mx-[-12.5px] !mb-[4.5rem] !mt-[-9rem] xl:!mb-[7rem] lg:!mb-[7rem] md:!mb-[7rem]">
<div class="md:w-6/12 lg:w-6/12 xl:w-3/12 w-full flex-[0_0_auto] max-w-full !px-[15px] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !mt-[25px]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body flex-[1_1_auto] p-[40px] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="svg-inject icon-svg !w-[2.2rem] !h-[2.2rem] solid-mono text-[#e668b3] text-fuchsia !mb-3"><path class="fill-primary" d="M152 0H29.33A29.32 29.32 0 000 29.33v165.33A29.32 29.32 0 0029.33 224h78.72l2.35-13.12a29.71 29.71 0 018.11-15.68l62.83-62.72V29.33A29.33 29.33 0 00152 0zM42.67 42.67h42.67a10.67 10.67 0 110 21.33H42.66a10.67 10.67 0 010-21.33zM96 149.33H42.67a10.67 10.67 0 110-21.33H96a10.67 10.67 0 110 21.33zm42.67-42.66h-96a10.67 10.67 0 010-21.34h96a10.67 10.67 0 110 21.34z"></path><path class="fill-secondary" d="M133.63 256a8 8 0 01-7.89-9.38l5.67-32.06a8 8 0 012.22-4.27l79.2-79.2c9.73-9.75 19.28-7.12 24.51-1.89l13.2 13.2a18.69 18.69 0 010 26.4l-79.2 79.2a7.83 7.83 0 01-4.27 2.22l-32 5.67a10.71 10.71 0 01-1.44.11zm32.05-13.65z"></path></svg>
<h4>{!! t('Yapay Zeka') !!}</h4>
<p class="!mb-2 flex-1 min-h-[4.5rem]">{!! t('Yapay zeka teknolojilerini kullanarak işlerinizi otomatikleştirebiliriz.') !!}</p>
<a href="{{ route('page.show', ['slug' => 'yapay-zeka']) }}" class="more hover !text-[#e668b3] focus:!text-[#e668b3] hover:!text-[#e668b3]">{!! t('Daha fazla bilgi edin') !!}</a>
<div class="flex flex-wrap glass-card-row !mb-[4.5rem] !mt-[-9rem] xl:!mb-[7rem] lg:!mb-[7rem] md:!mb-[7rem]">
<div class="w-full md:w-6/12 lg:w-6/12 xl:w-3/12 flex-[0_0_auto] max-w-full glass-card-col">
<a href="{{ route('page.show', ['slug' => 'yapay-zeka']) }}" class="card glass-card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body glass-card-body flex-[1_1_auto] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" class="svg-inject icon-svg !w-[1.8rem] !h-[1.8rem] md:!w-[2.2rem] md:!h-[2.2rem] solid-mono text-[#e668b3] text-fuchsia !mb-2 md:!mb-3"><path class="fill-primary" d="M152 0H29.33A29.32 29.32 0 000 29.33v165.33A29.32 29.32 0 0029.33 224h78.72l2.35-13.12a29.71 29.71 0 018.11-15.68l62.83-62.72V29.33A29.33 29.33 0 00152 0zM42.67 42.67h42.67a10.67 10.67 0 110 21.33H42.66a10.67 10.67 0 010-21.33zM96 149.33H42.67a10.67 10.67 0 110-21.33H96a10.67 10.67 0 110 21.33zm42.67-42.66h-96a10.67 10.67 0 010-21.34h96a10.67 10.67 0 110 21.34z"></path><path class="fill-secondary" d="M133.63 256a8 8 0 01-7.89-9.38l5.67-32.06a8 8 0 012.22-4.27l79.2-79.2c9.73-9.75 19.28-7.12 24.51-1.89l13.2 13.2a18.69 18.69 0 010 26.4l-79.2 79.2a7.83 7.83 0 01-4.27 2.22l-32 5.67a10.71 10.71 0 01-1.44.11zm32.05-13.65z"></path></svg>
<h4 class="!text-[14px] md:!text-[1.1rem] !mb-1 md:!mb-2">{!! t('Yapay Zeka') !!}</h4>
<p class="!mb-0 flex-1 min-h-[4rem] md:min-h-[4.5rem] !text-[11px] md:!text-[14px] !leading-snug md:!leading-relaxed">{!! t('Yapay zeka teknolojilerini kullanarak işlerinizi otomatikleştirebiliriz.') !!}</p>
</div>
<!--/.card-body -->
</div>
</a>
<!--/.card -->
</div>
<!--/column -->
<div class="md:w-6/12 lg:w-6/12 xl:w-3/12 w-full flex-[0_0_auto] max-w-full !px-[15px] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !mt-[25px]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body flex-[1_1_auto] p-[40px] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-inject icon-svg !w-[2.2rem] !h-[2.2rem] solid-mono text-[#a07cc5] text-violet !mb-3"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
<h4>{!! t('Web Uygulamaları') !!}</h4>
<p class="!mb-2 flex-1 min-h-[4.5rem]">{!! t('Web uygulamaları geliştirmek için gerekli olan tüm hizmetleri sunuyoruz.') !!}</p>
<a href="{{ route('page.show', ['slug' => 'web-uygulamalari']) }}" class="more hover !text-[#a07cc5] focus:!text-[#a07cc5] hover:!text-[#a07cc5]">{!! t('Daha fazla bilgi edin') !!}</a>
<div class="w-full md:w-6/12 lg:w-6/12 xl:w-3/12 flex-[0_0_auto] max-w-full glass-card-col">
<a href="{{ route('page.show', ['slug' => 'web-uygulamalari']) }}" class="card glass-card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body glass-card-body flex-[1_1_auto] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-inject icon-svg !w-[1.8rem] !h-[1.8rem] md:!w-[2.2rem] md:!h-[2.2rem] solid-mono text-[#a07cc5] text-violet !mb-2 md:!mb-3"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
<h4 class="!text-[14px] md:!text-[1.1rem] !mb-1 md:!mb-2">{!! t('Web Uygulamaları') !!}</h4>
<p class="!mb-0 flex-1 min-h-[4rem] md:min-h-[4.5rem] !text-[11px] md:!text-[14px] !leading-snug md:!leading-relaxed">{!! t('Web uygulamaları geliştirmek için gerekli olan tüm hizmetleri sunuyoruz.') !!}</p>
</div>
<!--/.card-body -->
</div>
</a>
<!--/.card -->
</div>
<!--/column -->
<div class="md:w-6/12 lg:w-6/12 xl:w-3/12 w-full flex-[0_0_auto] max-w-full !px-[15px] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !mt-[25px]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body flex-[1_1_auto] p-[40px] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="svg-inject icon-svg !w-[2.2rem] !h-[2.2rem] solid-mono text-[#f78b77] text-orange !mb-3"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"></path></svg>
<h4>{!! t('Müzik Prodüksiyon') !!}</h4>
<p class="!mb-2 flex-1 min-h-[4.5rem]">{!! t('Müzik prodüksiyonu, film müzikleri, kurumsal müzik çalışmaları ve benzeri alanlarda hizmet veriyoruz.') !!}</p>
<a href="{{ route('music-productions.index') }}" class="more hover !text-[#f78b77] focus:!text-[#f78b77] hover:!text-[#f78b77]">{!! t('Daha fazla bilgi edin') !!}</a>
<div class="w-full md:w-6/12 lg:w-6/12 xl:w-3/12 flex-[0_0_auto] max-w-full glass-card-col">
<a href="{{ route('music-productions.index') }}" class="card glass-card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body glass-card-body flex-[1_1_auto] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="svg-inject icon-svg !w-[1.8rem] !h-[1.8rem] md:!w-[2.2rem] md:!h-[2.2rem] solid-mono text-[#f78b77] text-orange !mb-2 md:!mb-3"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"></path></svg>
<h4 class="!text-[14px] md:!text-[1.1rem] !mb-1 md:!mb-2">{!! t('Müzik Prodüksiyon') !!}</h4>
<p class="!mb-0 flex-1 min-h-[4rem] md:min-h-[4.5rem] !text-[11px] md:!text-[14px] !leading-snug md:!leading-relaxed">{!! t('Müzik prodüksiyonu, film müzikleri, kurumsal müzik çalışmaları ve benzeri alanlarda hizmet veriyoruz.') !!}</p>
</div>
<!--/.card-body -->
</div>
</a>
<!--/.card -->
</div>
<!--/column -->
<div class="md:w-6/12 lg:w-6/12 xl:w-3/12 w-full flex-[0_0_auto] max-w-full !px-[15px] xl:!px-[12.5px] lg:!px-[12.5px] md:!px-[12.5px] !mt-[25px]">
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body flex-[1_1_auto] p-[40px] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-inject icon-svg !w-[2.2rem] !h-[2.2rem] solid-mono text-[#45c4a0] text-green !mb-3"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path></svg>
<h4>{!! t('Uygulama Geliştirme') !!}</h4>
<p class="!mb-2 flex-1 min-h-[4.5rem]">{!! t('Android, iOS, MacOS, Windows uygulamaları geliştiriyoruz.') !!}</p>
<a href="{{ route('page.show', ['slug' => 'uygulama-gelistirme']) }}" class="more hover !text-[#45c4a0] focus:!text-[#45c4a0] hover:!text-[#45c4a0]">{!! t('Daha fazla bilgi edin') !!}</a>
<div class="w-full md:w-6/12 lg:w-6/12 xl:w-3/12 flex-[0_0_auto] max-w-full glass-card-col">
<a href="{{ route('page.show', ['slug' => 'uygulama-gelistirme']) }}" class="card glass-card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)] h-full flex flex-col">
<div class="card-body glass-card-body flex-[1_1_auto] flex flex-col">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-inject icon-svg !w-[1.8rem] !h-[1.8rem] md:!w-[2.2rem] md:!h-[2.2rem] solid-mono text-[#45c4a0] text-green !mb-2 md:!mb-3"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path></svg>
<h4 class="!text-[14px] md:!text-[1.1rem] !mb-1 md:!mb-2">{!! t('Uygulama Geliştirme') !!}</h4>
<p class="!mb-0 flex-1 min-h-[4rem] md:min-h-[4.5rem] !text-[11px] md:!text-[14px] !leading-snug md:!leading-relaxed">{!! t('Android, iOS, MacOS, Windows uygulamaları geliştiriyoruz.') !!}</p>
</div>
<!--/.card-body -->
</div>
</a>
<!--/.card -->
</div>
<!--/column -->
@@ -59,3 +55,128 @@
<!-- /.container -->
</section>
<style>
.glass-card-row {
margin-left: -8px !important;
margin-right: -8px !important;
}
@media (min-width: 768px) {
.glass-card-row {
margin-left: -12.5px !important;
margin-right: -12.5px !important;
}
}
.glass-card-col {
padding-left: 8px !important;
padding-right: 8px !important;
margin-top: 16px !important;
}
@media (min-width: 768px) {
.glass-card-col {
padding-left: 12.5px !important;
padding-right: 12.5px !important;
margin-top: 25px !important;
}
}
.glass-card-body {
padding: 20px !important;
}
@media (min-width: 768px) {
.glass-card-body {
padding: 40px !important;
}
}
.glass-card {
position: relative !important;
border-radius: 16px !important;
border: none !important;
background: rgba(255, 255, 255, 0.6) !important;
overflow: visible !important;
isolation: isolate;
transition: all 0.3s ease-in-out;
color: inherit !important;
text-decoration: none !important;
}
@media (min-width: 768px) {
.glass-card {
border-radius: 30px !important;
}
}
.glass-card svg {
transition: all 0.3s ease-in-out;
}
.glass-card h4 {
color: #343f52;
transition: all 0.3s ease-in-out;
}
.glass-card:hover h4 {
color: white;
transition: all 0.3s ease-in-out;
text-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2);
}
.glass-card:hover svg {
filter: brightness(0) invert(1);
}
.glass-card p {
color: #60697b;
transition: all 0.3s ease-in-out;
}
.glass-card:hover p {
color: rgba(255, 255, 255, 0.9);
}
.glass-card::before {
content: '';
position: absolute;
inset: 0;
z-index: 1;
overflow: hidden;
border-radius: 16px;
pointer-events: none;
-webkit-box-shadow: inset 2px 2px 0px -2px rgba(255, 255, 255, 0.7), inset 0 0 3px 1px rgba(255, 255, 255, 0.7);
box-shadow: inset 2px 2px 0px -2px rgba(255, 255, 255, 0.7), inset 0 0 3px 1px rgba(255, 255, 255, 0.7);
}
@media (min-width: 768px) {
.glass-card::before {
border-radius: 30px;
}
}
.glass-card::after {
content: '';
position: absolute;
z-index: -1;
inset: 0;
border-radius: 16px;
-webkit-backdrop-filter: blur(15px);
backdrop-filter: blur(15px);
overflow: hidden;
}
@media (min-width: 768px) {
.glass-card::after {
border-radius: 30px;
}
}
.glass-card:hover {
transform: translateY(-5px);
background: rgba(247, 108, 38, 0.85) !important;
box-shadow: 0 1rem 3rem rgba(247, 108, 38, 0.3) !important;
}
</style>
@@ -12,7 +12,7 @@
<!-- /.row -->
<div class="flex flex-wrap mx-[-15px] xl:mx-[-35px] lg:mx-[-20px] items-center">
<div class="lg:w-5/12 xl:w-4/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !ml-auto hidden xl:!flex lg:!flex xl:!px-[35px] lg:!px-[20px]">
<div class="img-mask mask-3"><img src="{{ setting('home_testimonials_image', asset('assets/img/photos/about28.webp')) }}" alt="image" loading="lazy"></div>
<div class="img-mask mask-3"><img src="{{ setting('home_testimonials_image', asset('assets/img/photos/about7.webp')) }}" alt="image" loading="lazy"></div>
</div>
<!--/column -->
<div class="lg:w-6/12 xl:w-6/12 xxl:w-5/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mr-auto xl:!px-[35px] lg:!px-[20px]">
@@ -1226,19 +1226,6 @@
</div>
</div>
</div>
</section>class="btn btn-gradient gradient-7 !rounded-[0.8rem] !px-6 !text-white">{!! t('Projenizi Konuşalım') !!}</a>
</div>
</div>
<div class="xl:w-6/12 lg:w-6/12 w-full flex-[0_0_auto] max-w-full !ml-auto !mt-lg-n15 xl:!mt-[-10rem] lg:!mt-[-5rem] !mt-[50px]" data-cue="slideInRight" data-delay="300">
<figure class="m-0 p-0">
<picture>
<source srcset="{{ asset('assets/img/illustrations/i12.png') }}" type="image/webp">
<img class="web-dev-illustration web-dev-illustration--cta w-full max-w-full !h-auto" src="{{ asset('assets/img/illustrations/i12.png') }}" alt="{!! t('Trunçgil Teknoloji web geliştirme') !!}" width="1024" height="1024" loading="lazy" decoding="async">
</picture>
</figure>
</div>
</div>
</div>
</section>
@endsection
+25
View File
@@ -120,8 +120,24 @@ Route::get('/stajyer/giris', [\App\Http\Controllers\CareerController::class, 'in
Route::post('/stajyer/giris', [\App\Http\Controllers\CareerController::class, 'internLogin'])->name('intern.login.submit');
Route::get('/stajyer/panel', [\App\Http\Controllers\CareerController::class, 'internDashboard'])->name('intern.dashboard');
Route::post('/stajyer/form-yukle', [\App\Http\Controllers\CareerController::class, 'uploadInternshipForm'])->name('intern.upload-form');
Route::post('/stajyer/github-kaydet', [\App\Http\Controllers\CareerController::class, 'saveGithubRepo'])->name('intern.save-github');
Route::get('/stajyer/gunluk-indir', [\App\Http\Controllers\CareerController::class, 'downloadMarkdown'])->name('intern.download-journal');
Route::post('/stajyer/defteri-kaydet', [\App\Http\Controllers\CareerController::class, 'saveJournalEntry'])->name('intern.save-journal');
Route::get('/stajyer/defteri-yazdir', [\App\Http\Controllers\CareerController::class, 'printJournal'])->name('intern.print-journal');
Route::get('/staj-dogrulama/{code}', [\App\Http\Controllers\CareerController::class, 'verifyCertificate'])->name('internship.verify');
Route::post('/stajyer/cikis', [\App\Http\Controllers\CareerController::class, 'internLogout'])->name('intern.logout');
// Intern Admin Portal
Route::get('/stajyer/admin/giris', [\App\Http\Controllers\CareerController::class, 'internAdminLoginForm'])->name('intern.admin.login');
Route::post('/stajyer/admin/giris', [\App\Http\Controllers\CareerController::class, 'internAdminLogin'])->name('intern.admin.login.submit');
Route::get('/stajyer/admin/panel', [\App\Http\Controllers\CareerController::class, 'internAdminDashboard'])->name('intern.admin.dashboard');
Route::get('/stajyer/admin/journal-entry', [\App\Http\Controllers\CareerController::class, 'getJournalEntry'])->name('intern.admin.get-journal-entry');
Route::post('/stajyer/admin/toggle-approval', [\App\Http\Controllers\CareerController::class, 'toggleJournalApproval'])->name('intern.admin.toggle-journal-approval');
Route::get('/stajyer/admin/journal-details', [\App\Http\Controllers\CareerController::class, 'getInternJournalDetails'])->name('intern.admin.get-journal-details');
Route::post('/stajyer/admin/toggle-notebook-signature', [\App\Http\Controllers\CareerController::class, 'toggleNotebookSignature'])->name('intern.admin.toggle-notebook-signature');
Route::post('/stajyer/admin/cikis', [\App\Http\Controllers\CareerController::class, 'internAdminLogout'])->name('intern.admin.logout');
// Payment Process
Route::post('/payment/process', [\App\Http\Controllers\PaymentController::class, 'process'])->name('payment.process');
Route::any('/payment/success', [\App\Http\Controllers\PaymentController::class, 'success'])->name('payment.success');
@@ -155,8 +171,17 @@ Route::get('/sitemap.xml', [\App\Http\Controllers\SitemapController::class, 'ind
Route::get('/teklif/{slug}', [\App\Http\Controllers\ProposalController::class, 'show'])->name('proposals.show');
Route::post('/teklif/{slug}/action', [\App\Http\Controllers\ProposalController::class, 'action'])->name('proposals.action');
// Privacy Policy Alternatives
Route::get('/privacy-policy', function () {
return app(PageController::class)->show('privacy');
});
Route::get('/gizlilik-politikasi', function () {
return app(PageController::class)->show('privacy');
});
// Nested Pages (Parent/Child)
Route::get('/{parentSlug}/{slug}', [PageController::class, 'showNested'])->name('page.show_nested');
// Pages (en sonda olmalı - catch-all)
Route::get('/{slug}', [PageController::class, 'show'])->name('page.show');
+383
View File
@@ -0,0 +1,383 @@
// Tab Switch Logic
document.addEventListener('DOMContentLoaded', function() {
const tabButtons = document.querySelectorAll('.tab-btn');
const tabPanels = document.querySelectorAll('.tab-panel');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
// Deactivate active tab
tabButtons.forEach(btn => {
btn.classList.remove('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
btn.classList.add('bg-transparent', 'border-transparent', 'text-slate-600');
btn.querySelector('.tab-icon').classList.remove('bg-blue-600', 'text-white');
btn.querySelector('.tab-icon').classList.add('bg-slate-50', 'text-slate-500');
btn.setAttribute('aria-selected', 'false');
});
// Hide active panels
tabPanels.forEach(panel => {
panel.classList.add('hidden');
});
// Activate clicked tab
button.classList.add('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
button.classList.remove('bg-transparent', 'border-transparent', 'text-slate-600');
button.querySelector('.tab-icon').classList.add('bg-blue-600', 'text-white');
button.querySelector('.tab-icon').classList.remove('bg-slate-50', 'text-slate-500');
button.setAttribute('aria-selected', 'true');
// Show targets panel
const targetId = button.getAttribute('data-tab-target');
const targetPanel = document.getElementById(targetId + '-panel');
if (targetPanel) {
targetPanel.classList.remove('hidden');
}
});
});
let quill;
// Tab Switch Logic
document.addEventListener('DOMContentLoaded', function() {
// Initialize Quill editor
quill = new Quill('#editor-content-quill', {
theme: 'snow',
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['clean'] // remove formatting button
]
}
});
const tabButtons = document.querySelectorAll('.tab-btn');
const tabPanels = document.querySelectorAll('.tab-panel');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
// Deactivate active tab
tabButtons.forEach(btn => {
btn.classList.remove('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
btn.classList.add('bg-transparent', 'border-transparent', 'text-slate-600');
btn.querySelector('.tab-icon').classList.remove('bg-blue-600', 'text-white');
btn.querySelector('.tab-icon').classList.add('bg-slate-50', 'text-slate-500');
btn.setAttribute('aria-selected', 'false');
});
// Hide active panels
tabPanels.forEach(panel => {
panel.classList.add('hidden');
});
// Activate clicked tab
button.classList.add('active', 'bg-blue-50', 'border-blue-100', 'text-blue-900');
button.classList.remove('bg-transparent', 'border-transparent', 'text-slate-600');
button.querySelector('.tab-icon').classList.add('bg-blue-600', 'text-white');
button.querySelector('.tab-icon').classList.remove('bg-slate-50', 'text-slate-500');
button.setAttribute('aria-selected', 'true');
// Show targets panel
const targetId = button.getAttribute('data-tab-target');
const targetPanel = document.getElementById(targetId + '-panel');
if (targetPanel) {
targetPanel.classList.remove('hidden');
}
});
});
// Handle initial state of date logic
calculateEndDateFrontend();
// Fetch Github commits in memory if repo is set
fetchAllCommitsInMemory();
// Select first day of the notebook workspace on load
selectNotebookDay(0);
});
const githubRepoUrl = "dummy";
let allGithubCommits = {};
let activeDayIdx = 0;
function fetchAllCommitsInMemory() {
if (!githubRepoUrl) return;
let repoClean = githubRepoUrl.replace(/https?:\/\/(www\.)?github\.com\//i, '');
repoClean = repoClean.replace(/\/$/, '');
const parts = repoClean.split('/');
if (parts.length < 2) return;
const owner = parts[0];
const repo = parts[1].replace(/\.git$/i, '');
fetch(`https://api.github.com/repos/${owner}/${repo}/commits?per_page=100`)
.then(response => {
if (!response.ok) throw new Error('API Error');
return response.json();
})
.then(commits => {
if (!Array.isArray(commits)) return;
commits.forEach(item => {
const dateStr = item.commit.author.date;
if (dateStr) {
const dateOnly = dateStr.substring(0, 10);
if (!allGithubCommits[dateOnly]) {
allGithubCommits[dateOnly] = [];
}
allGithubCommits[dateOnly].push(item);
}
});
})
.catch(err => {
console.warn('Commits could not be pre-loaded in memory.', err);
});
}
function selectNotebookDay(idx) {
const oldBtn = document.getElementById(`btn-day-${activeDayIdx}`);
if (oldBtn) {
oldBtn.classList.remove('border-blue-200', 'bg-blue-50/50');
oldBtn.classList.add('border-slate-100', 'bg-white');
const oldTitle = oldBtn.querySelector('span.text-xs');
if (oldTitle) {
oldTitle.classList.remove('text-blue-700');
oldTitle.classList.add('text-slate-800');
}
}
activeDayIdx = idx;
const newBtn = document.getElementById(`btn-day-${idx}`);
if (newBtn) {
newBtn.classList.add('border-blue-200', 'bg-blue-50/50');
newBtn.classList.remove('border-slate-100', 'bg-white');
const newTitle = newBtn.querySelector('span.text-xs');
if (newTitle) {
newTitle.classList.add('text-blue-700');
newTitle.classList.remove('text-slate-800');
}
const dayNum = newBtn.getAttribute('data-day-num');
const dateVal = newBtn.getAttribute('data-date');
const dateFormatted = newBtn.getAttribute('data-formatted-date');
const savedContent = newBtn.getAttribute('data-saved-content');
document.getElementById('editor-day-title').textContent = `${dayNum}. Gün`;
document.getElementById('editor-day-date').textContent = dateFormatted;
quill.root.innerHTML = savedContent || '';
document.getElementById('save-status').textContent = '';
// Future validation
const dateParts = dateVal.split('-');
const selectedDate = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
const today = new Date();
today.setHours(0,0,0,0);
const isFuture = selectedDate > today;
const warningBlock = document.getElementById('future-warning');
const saveBtn = document.getElementById('save-btn');
const gitBtn = document.querySelector('button[onclick="fillFromGithub()"]');
if (isFuture) {
warningBlock.classList.remove('hidden');
quill.enable(false);
saveBtn.disabled = true;
saveBtn.classList.add('opacity-50', 'cursor-not-allowed');
if (gitBtn) {
gitBtn.disabled = true;
gitBtn.classList.add('opacity-50', 'cursor-not-allowed');
}
} else {
warningBlock.classList.add('hidden');
quill.enable(true);
saveBtn.disabled = false;
saveBtn.classList.remove('opacity-50', 'cursor-not-allowed');
if (gitBtn) {
gitBtn.disabled = false;
gitBtn.classList.remove('opacity-50', 'cursor-not-allowed');
}
}
}
}
function fillFromGithub() {
const btn = document.getElementById(`btn-day-${activeDayIdx}`);
if (!btn) return;
const dateVal = btn.getAttribute('data-date');
const dayCommits = allGithubCommits[dateVal] || [];
if (dayCommits.length === 0) {
alert("Bu staj gününe ait GitHub commit'i bulunamadı. Lütfen commit attığınızdan ve tarihin doğru olduğundan emin olun.");
return;
}
let commitHtml = "<ul>";
const sorted = [...dayCommits].reverse();
sorted.forEach(c => {
const msg = c.commit.message;
const sha = c.sha.substring(0, 7);
const authorDate = new Date(c.commit.author.date);
const time = authorDate.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
commitHtml += `<li><strong>[${time}]</strong> (<em>${sha}</em>) ${msg}</li>`;
});
commitHtml += "</ul>";
const currentHtml = quill.root.innerHTML.trim();
if (currentHtml !== "" && currentHtml !== "<p><br></p>") {
if (confirm("Mevcut rapor içeriğinizin sonuna bu günün commitlerini eklemek ister misiniz? (Hayır derseniz mevcut içerik silinip commitler yazılır.)")) {
quill.root.innerHTML = currentHtml + "<br><br>" + commitHtml;
} else {
quill.root.innerHTML = commitHtml;
}
} else {
quill.root.innerHTML = commitHtml;
}
}
function saveActiveDay() {
const btn = document.getElementById(`btn-day-${activeDayIdx}`);
if (!btn) return;
const dayNum = btn.getAttribute('data-day-num');
const dateVal = btn.getAttribute('data-date');
let contentVal = quill.root.innerHTML;
if (contentVal === '<p><br></p>') {
contentVal = '';
}
const statusText = document.getElementById('save-status');
statusText.textContent = "Kaydediliyor...";
statusText.className = "text-xs font-semibold text-slate-400 animate-pulse";
fetch(""dummy"", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": ""dummy""
},
body: JSON.stringify({
day_number: dayNum,
date: dateVal,
content: contentVal
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
statusText.textContent = "Başarıyla kaydedildi.";
statusText.className = "text-xs font-semibold text-green-600";
btn.setAttribute('data-saved-content', contentVal);
const badge = document.getElementById(`badge-day-${activeDayIdx}`);
if (badge) {
if (contentVal.trim() !== '') {
badge.textContent = 'DOLU';
badge.className = "inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase bg-green-50 text-green-700 border border-green-200";
} else {
badge.textContent = 'BOŞ';
badge.className = "inline-flex px-2 py-0.5 rounded-lg text-[9px] font-extrabold uppercase bg-slate-50 text-slate-400 border border-slate-200";
}
}
} else {
statusText.textContent = data.message || "Kaydedilemedi.";
statusText.className = "text-xs font-semibold text-red-600";
}
})
.catch(err => {
console.error(err);
statusText.textContent = "Bağlantı hatası oluştu.";
statusText.className = "text-xs font-semibold text-red-600";
});
}
function openPrintModal() {
const modal = document.getElementById('print-modal');
const card = document.getElementById('print-modal-card');
modal.classList.remove('hidden');
setTimeout(() => {
card.classList.remove('scale-95', 'opacity-0');
card.classList.add('scale-100', 'opacity-100');
}, 10);
}
function closePrintModal() {
const modal = document.getElementById('print-modal');
const card = document.getElementById('print-modal-card');
card.classList.remove('scale-100', 'opacity-100');
card.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.classList.add('hidden');
}, 300);
}
function handleFileSelected(input) {
const fileNameElement = document.getElementById('selected-file-name');
const submitBtn = document.getElementById('submit-upload-btn');
if (input.files && input.files.length > 0) {
const file = input.files[0];
const fileSizeMB = file.size / (1024 * 1024);
if (fileSizeMB > 50) {
alert('Dosya boyutu 50MB\'ı aşamaz.');
input.value = '';
fileNameElement.textContent = '';
submitBtn.disabled = true;
submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
return;
}
fileNameElement.textContent = 'Seçilen dosya: ' + file.name + ' (' + fileSizeMB.toFixed(2) + ' MB)';
submitBtn.disabled = false;
submitBtn.classList.remove('opacity-50', 'cursor-not-allowed');
} else {
fileNameElement.textContent = '';
submitBtn.disabled = true;
submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
}
}
function calculateEndDateFrontend() {
const startDateVal = document.getElementById('internship_start_date').value;
const totalDaysVal = document.getElementById('internship_total_days').value;
const endDateInput = document.getElementById('internship_end_date');
if (!startDateVal || !totalDaysVal || totalDaysVal <= 0) {
return;
}
let date = new Date(startDateVal);
let daysToAdd = parseInt(totalDaysVal);
let count = 0;
let endDate = new Date(startDateVal);
while (count < daysToAdd) {
const dayOfWeek = date.getDay();
if (dayOfWeek === 6 || dayOfWeek === 0) { // 6 = Saturday, 0 = Sunday
date.setDate(date.getDate() + 1);
continue;
}
endDate = new Date(date);
date.setDate(date.getDate() + 1);
count++;
}
// Format to YYYY-MM-DD
const yyyy = endDate.getFullYear();
const mm = String(endDate.getMonth() + 1).padStart(2, '0');
const dd = String(endDate.getDate()).padStart(2, '0');
endDateInput.value = `${yyyy}-${mm}-${dd}`;
}
function copyToClipboard(elementId, btn) {
const text = document.getElementById(elementId).textContent;
navigator.clipboard.writeText(text).then(() => {
const icon = btn.querySelector('i');
const originalClass = icon.className;
icon.className = 'uil uil-check text-green-500';
setTimeout(() => {
icon.className = originalClass;
}, 2000);
});
}