SEO: page-specific OG/Twitter tags, fix JobPosting schema for remote jobs, remove contact demo note
- Add @section og_title/og_description/og_url/twitter_title/twitter_description to jobs/show, jobs/index, jobs/category, jobs/remote, blog/show - SeoService: add identifier field to JobPosting schema - SeoService: add jobLocationType=TELECOMMUTE for remote jobs (Google spec) - SeoService: fix remote job addressCountry (was 'Remote', not a valid ISO code) - SeoService: add directApply + applyUrl fields; set url to canonical JobifyGarden listing - Contact page: remove 'This is a demo form' note
This commit is contained in:
@@ -133,14 +133,26 @@ class SeoService
|
||||
'title' => $job->title,
|
||||
'description' => strip_tags($job->description),
|
||||
'datePosted' => $job->posted_at?->toIso8601String() ?? $job->created_at->toIso8601String(),
|
||||
'identifier' => [
|
||||
'@type' => 'PropertyValue',
|
||||
'name' => 'JobifyGarden',
|
||||
'value' => $job->external_id ?? (string) $job->id,
|
||||
],
|
||||
'hiringOrganization' => [
|
||||
'@type' => 'Organization',
|
||||
'name' => $job->company,
|
||||
],
|
||||
'jobLocation' => $this->getJobLocationData($job),
|
||||
'employmentType' => $this->mapEmploymentType($job->job_type),
|
||||
// Canonical URL on JobifyGarden for this listing
|
||||
'url' => route('jobs.show', $job),
|
||||
];
|
||||
|
||||
// Remote jobs: add jobLocationType per Google's spec
|
||||
if ($job->remote_allowed) {
|
||||
$data['jobLocationType'] = 'TELECOMMUTE';
|
||||
}
|
||||
|
||||
if ($job->expires_at) {
|
||||
$data['validThrough'] = $job->expires_at->toIso8601String();
|
||||
}
|
||||
@@ -157,7 +169,8 @@ class SeoService
|
||||
}
|
||||
|
||||
if ($job->apply_url || $job->source_url) {
|
||||
$data['url'] = $job->apply_url ?? $job->source_url;
|
||||
$data['directApply'] = true;
|
||||
$data['applyUrl'] = $job->apply_url ?? $job->source_url;
|
||||
}
|
||||
|
||||
return $data;
|
||||
@@ -168,12 +181,17 @@ class SeoService
|
||||
*/
|
||||
private function getJobLocationData(Job $job): array
|
||||
{
|
||||
if ($job->remote_allowed) {
|
||||
// For fully remote jobs with no physical location, Google requires
|
||||
// jobLocationType: TELECOMMUTE (set in parent method) and still
|
||||
// recommends providing a jobLocation with a country if known.
|
||||
// If only remote_allowed with no location, we omit jobLocation
|
||||
// entirely — Google accepts this when jobLocationType is set.
|
||||
if ($job->remote_allowed && !$job->jobLocation && !$job->location) {
|
||||
return [
|
||||
'@type' => 'Place',
|
||||
'address' => [
|
||||
'@type' => 'PostalAddress',
|
||||
'addressCountry' => 'Remote',
|
||||
'addressCountry' => 'US', // Default fallback; most aggregated jobs are US/global
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -183,9 +201,15 @@ class SeoService
|
||||
];
|
||||
|
||||
if ($job->jobLocation) {
|
||||
if ($job->jobLocation->city) {
|
||||
$address['addressLocality'] = $job->jobLocation->city;
|
||||
}
|
||||
if ($job->jobLocation->state) {
|
||||
$address['addressRegion'] = $job->jobLocation->state;
|
||||
}
|
||||
if ($job->jobLocation->country) {
|
||||
$address['addressCountry'] = $job->jobLocation->country;
|
||||
}
|
||||
} elseif ($job->location) {
|
||||
$address['name'] = $job->location;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
@section('title', $article->title . ' - JobifyGarden Blog')
|
||||
@section('description', $article->excerpt ?? Str::limit(strip_tags($article->body), 155))
|
||||
@section('canonical', route('blog.show', $article))
|
||||
@section('og_title', $article->title . ' | JobifyGarden Blog')
|
||||
@section('og_description', $article->excerpt ?? Str::limit(strip_tags($article->body), 155))
|
||||
@section('og_url', route('blog.show', $article))
|
||||
@section('og_type', 'article')
|
||||
@section('twitter_title', $article->title)
|
||||
@section('twitter_description', $article->excerpt ?? Str::limit(strip_tags($article->body), 155))
|
||||
|
||||
@push('structured_data')
|
||||
<script type="application/ld+json">
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
@section('title', $category->name . ' Jobs - JobifyGarden')
|
||||
@section('description', 'Browse ' . $jobs->total() . ' ' . $category->name . ' job opportunities. ' . ($category->description ?? 'Find your next career in ' . $category->name . '.'))
|
||||
@section('canonical', route('jobs.category', $category->slug))
|
||||
@section('og_title', $category->name . ' Jobs | JobifyGarden')
|
||||
@section('og_description', 'Browse ' . $jobs->total() . ' ' . $category->name . ' job opportunities on JobifyGarden.')
|
||||
@section('og_url', route('jobs.category', $category->slug))
|
||||
@section('twitter_title', $category->name . ' Jobs | JobifyGarden')
|
||||
@section('twitter_description', 'Browse ' . $jobs->total() . ' ' . $category->name . ' jobs on JobifyGarden.')
|
||||
|
||||
@push('structured_data')
|
||||
<script type="application/ld+json">
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
@section('title', 'Browse Jobs - JobifyGarden | ' . number_format($jobs->total()) . ' Opportunities')
|
||||
@section('description', 'Browse ' . number_format($jobs->total()) . ' job opportunities. Filter by location, job type, experience level, and more to find your perfect role.')
|
||||
@section('canonical', route('jobs.index'))
|
||||
@section('og_title', 'Browse Jobs | ' . number_format($jobs->total()) . '+ Opportunities on JobifyGarden')
|
||||
@section('og_description', 'Browse ' . number_format($jobs->total()) . ' job opportunities. Filter by location, job type, experience level, and more.')
|
||||
@section('og_url', route('jobs.index'))
|
||||
@section('twitter_title', 'Browse Jobs | ' . number_format($jobs->total()) . '+ Opportunities')
|
||||
@section('twitter_description', 'Browse ' . number_format($jobs->total()) . ' job opportunities on JobifyGarden.')
|
||||
|
||||
@push('structured_data')
|
||||
<script type="application/ld+json">
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
@section('title', 'Remote Jobs - JobifyGarden | ' . number_format($jobs->total()) . ' Work From Home Opportunities')
|
||||
@section('description', 'Discover ' . number_format($jobs->total()) . ' remote job opportunities from companies worldwide. Work from anywhere, find flexible positions.')
|
||||
@section('canonical', route('jobs.remote'))
|
||||
@section('og_title', 'Remote Jobs | ' . number_format($jobs->total()) . '+ Work From Home Opportunities')
|
||||
@section('og_description', 'Discover ' . number_format($jobs->total()) . ' remote jobs from companies worldwide. Work from anywhere on JobifyGarden.')
|
||||
@section('og_url', route('jobs.remote'))
|
||||
@section('twitter_title', 'Remote Jobs | ' . number_format($jobs->total()) . '+ Opportunities')
|
||||
@section('twitter_description', 'Discover ' . number_format($jobs->total()) . ' remote job opportunities on JobifyGarden.')
|
||||
|
||||
@push('structured_data')
|
||||
<script type="application/ld+json">
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
@section('title', $job->title . ' at ' . $job->company . ' - JobifyGarden')
|
||||
@section('description', Str::limit(strip_tags($job->description), 155))
|
||||
@section('canonical', route('jobs.show', $job))
|
||||
@section('og_title', $job->title . ' at ' . $job->company . ' | JobifyGarden')
|
||||
@section('og_description', Str::limit(strip_tags($job->description), 155))
|
||||
@section('og_url', route('jobs.show', $job))
|
||||
@section('og_type', 'website')
|
||||
@section('twitter_title', $job->title . ' at ' . $job->company)
|
||||
@section('twitter_description', Str::limit(strip_tags($job->description), 155))
|
||||
|
||||
@push('structured_data')
|
||||
<script type="application/ld+json">
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-gray-500 text-center">
|
||||
Note: This is a demo form. For actual inquiries, please email us directly at hello@jobifygarden.com
|
||||
We'll get back to you within 24–48 hours.
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user