diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php
index eb8c3a2..d0a6454 100644
--- a/app/Http/Controllers/ToolController.php
+++ b/app/Http/Controllers/ToolController.php
@@ -129,6 +129,24 @@ public function index(): View
'route' => 'tools.cron',
'icon' => 'clock',
],
+ [
+ 'name' => 'JWT Decoder',
+ 'description' => 'Decode and inspect JSON Web Tokens',
+ 'route' => 'tools.jwt',
+ 'icon' => 'jwt',
+ ],
+ [
+ 'name' => 'Timestamp Converter',
+ 'description' => 'Convert Unix timestamps to dates and vice versa',
+ 'route' => 'tools.timestamp',
+ 'icon' => 'timestamp',
+ ],
+ [
+ 'name' => 'Diff Checker',
+ 'description' => 'Compare two texts and highlight differences',
+ 'route' => 'tools.diff',
+ 'icon' => 'diff',
+ ],
];
return view('home', compact('tools'));
@@ -233,4 +251,19 @@ public function cron(): View
{
return view('tools.cron-parser');
}
+
+ public function jwt(): View
+ {
+ return view('tools.jwt');
+ }
+
+ public function timestamp(): View
+ {
+ return view('tools.timestamp');
+ }
+
+ public function diff(): View
+ {
+ return view('tools.diff');
+ }
}
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index 2a21f80..4e1851a 100644
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -90,7 +90,6 @@
@break
-
@case('calculator')
@break
+ @case('jwt')
+
+ @break
+ @case('timestamp')
+
+ @break
+ @case('diff')
+
+ @break
@endswitch
diff --git a/resources/views/tools/diff.blade.php b/resources/views/tools/diff.blade.php
new file mode 100644
index 0000000..3b2869c
--- /dev/null
+++ b/resources/views/tools/diff.blade.php
@@ -0,0 +1,336 @@
+@extends('layouts.app')
+
+@section('title', 'Diff Checker - Compare Text Online | Dev Tools')
+@section('meta_description', 'Free online diff checker. Compare two texts side by side, highlight differences, and see additions, deletions, and changes. Perfect for code review.')
+@section('meta_keywords', 'diff checker, text compare, diff tool, compare files, code diff, text difference, online diff')
+
+@push('schema')
+
+@endpush
+
+@section('content')
+
+
+
+
Diff Checker
+
Compare two texts and highlight differences
+
+
← Back
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ added
+
+
+ removed
+
+
+ unchanged
+
+
+
+
+
+
+
+ Original
+
+
+ Modified
+
+
+
+
+
+
+
+
+
Enter text in both fields and click Compare to see differences
+
+
+@endsection
+
+@push('scripts')
+
+@endpush
diff --git a/resources/views/tools/jwt.blade.php b/resources/views/tools/jwt.blade.php
new file mode 100644
index 0000000..4910a1b
--- /dev/null
+++ b/resources/views/tools/jwt.blade.php
@@ -0,0 +1,336 @@
+@extends('layouts.app')
+
+@section('title', 'JWT Decoder - Decode JSON Web Tokens Online | Dev Tools')
+@section('meta_description', 'Free online JWT decoder. Decode and inspect JSON Web Tokens, view header and payload, check expiration status. No data sent to server.')
+@section('meta_keywords', 'jwt decoder, json web token, jwt parser, jwt debugger, decode jwt, jwt viewer, token decoder, jwt online')
+
+@push('schema')
+
+@endpush
+
+@section('content')
+
+
+
+
JWT Decoder
+
Decode and inspect JSON Web Tokens
+
+
← Back
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..
+
+
+
+
+ Header
+
+
+
+ Payload
+
+
+
+ Signature
+
+
+
+
+
+
+
+
+
+
Security Note
+
JWTs are decoded client-side. No data is sent to any server. Never share tokens containing sensitive information.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Paste a JWT to see the header
+
+
+
+
+
+
+
Paste a JWT to see the payload
+
+
+
+
+
+
+
+@endsection
+
+@push('scripts')
+
+@endpush
diff --git a/resources/views/tools/timestamp.blade.php b/resources/views/tools/timestamp.blade.php
new file mode 100644
index 0000000..7f78f80
--- /dev/null
+++ b/resources/views/tools/timestamp.blade.php
@@ -0,0 +1,383 @@
+@extends('layouts.app')
+
+@section('title', 'Unix Timestamp Converter - Convert Timestamps Online | Dev Tools')
+@section('meta_description', 'Free online Unix timestamp converter. Convert timestamps to human-readable dates and vice versa. Supports seconds, milliseconds, and multiple timezones.')
+@section('meta_keywords', 'unix timestamp, timestamp converter, epoch converter, date to timestamp, timestamp to date, unix time, epoch time')
+
+@push('schema')
+
+@endpush
+
+@section('content')
+
+
+
+
Timestamp Converter
+
Convert Unix timestamps to dates and vice versa
+
+
← Back
+
+
+
+
+
+
Current Unix Timestamp
+
+
+
+
+
+
+
+
+
Timestamp to Date
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Local Time
+
+
+
+ UTC
+
+
+
+ ISO 8601
+
+
+
+ Relative
+
+
+
+
+
+
+
+
+
Date to Timestamp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@endsection
+
+@push('scripts')
+
+@endpush
diff --git a/routes/web.php b/routes/web.php
index ee8cb4e..63e04f2 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -17,18 +17,18 @@
Route::get('/url', [ToolController::class, 'url'])->name('url');
Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor');
Route::get('/regex', [ToolController::class, 'regex'])->name('regex');
-
Route::get('/base-converter', [ToolController::class, 'baseConverter'])->name('base-converter');
Route::get('/slug-generator', [ToolController::class, 'slugGenerator'])->name('slug-generator');
Route::get('/color-picker', [ToolController::class, 'colorPicker'])->name('color-picker');
-
Route::get('/qr-code', [ToolController::class, 'qrCode'])->name('qr-code');
-
Route::get('/html-entity', [ToolController::class, 'htmlEntity'])->name('html-entity');
Route::get('/text-case', [ToolController::class, 'textCase'])->name('text-case');
Route::get('/password', [ToolController::class, 'password'])->name('password');
Route::get('/lorem', [ToolController::class, 'lorem'])->name('lorem');
Route::get('/cron', [ToolController::class, 'cron'])->name('cron');
+ Route::get('/jwt', [ToolController::class, 'jwt'])->name('jwt');
+ Route::get('/timestamp', [ToolController::class, 'timestamp'])->name('timestamp');
+ Route::get('/diff', [ToolController::class, 'diff'])->name('diff');
});
// Static Pages
@@ -50,18 +50,18 @@
['loc' => route('tools.url'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'],
['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'],
-
['loc' => route('tools.base-converter'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.slug-generator'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.color-picker'), 'priority' => '0.8', 'changefreq' => 'monthly'],
-
['loc' => route('tools.qr-code'), 'priority' => '0.8', 'changefreq' => 'monthly'],
-
['loc' => route('tools.html-entity'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.text-case'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.password'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.lorem'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('tools.cron'), 'priority' => '0.8', 'changefreq' => 'monthly'],
+ ['loc' => route('tools.jwt'), 'priority' => '0.8', 'changefreq' => 'monthly'],
+ ['loc' => route('tools.timestamp'), 'priority' => '0.8', 'changefreq' => 'monthly'],
+ ['loc' => route('tools.diff'), 'priority' => '0.8', 'changefreq' => 'monthly'],
['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'],
['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'],
];
diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php
index ae1bc1b..d99e52e 100644
--- a/tests/Feature/WebRoutesTest.php
+++ b/tests/Feature/WebRoutesTest.php
@@ -29,18 +29,18 @@ public function test_home_page_displays_all_tools(): void
$response->assertSee('URL Encoder');
$response->assertSee('Code Editor');
$response->assertSee('Regex Tester');
-
$response->assertSee('Base Converter');
$response->assertSee('Slug Generator');
$response->assertSee('Color Picker');
-
$response->assertSee('QR Code Generator');
-
$response->assertSee('HTML Entity Encoder');
$response->assertSee('Text Case Converter');
$response->assertSee('Password Generator');
$response->assertSee('Lorem Ipsum Generator');
$response->assertSee('Cron Parser');
+ $response->assertSee('JWT Decoder');
+ $response->assertSee('Timestamp Converter');
+ $response->assertSee('Diff Checker');
}
public function test_home_page_has_tool_links(): void
@@ -58,18 +58,18 @@ public function test_home_page_has_tool_links(): void
$response->assertSee('href="' . route('tools.url') . '"', false);
$response->assertSee('href="' . route('tools.code-editor') . '"', false);
$response->assertSee('href="' . route('tools.regex') . '"', false);
-
$response->assertSee('href="' . route('tools.base-converter') . '"', false);
$response->assertSee('href="' . route('tools.slug-generator') . '"', false);
$response->assertSee('href="' . route('tools.color-picker') . '"', false);
-
$response->assertSee('href="' . route('tools.qr-code') . '"', false);
-
$response->assertSee('href="' . route('tools.html-entity') . '"', false);
$response->assertSee('href="' . route('tools.text-case') . '"', false);
$response->assertSee('href="' . route('tools.password') . '"', false);
$response->assertSee('href="' . route('tools.lorem') . '"', false);
$response->assertSee('href="' . route('tools.cron') . '"', false);
+ $response->assertSee('href="' . route('tools.jwt') . '"', false);
+ $response->assertSee('href="' . route('tools.timestamp') . '"', false);
+ $response->assertSee('href="' . route('tools.diff') . '"', false);
}
public function test_csv_tool_page_loads(): void
@@ -458,9 +458,67 @@ public function test_cron_tool_has_required_elements(): void
$response->assertSee('Next Run Times');
}
+ public function test_jwt_tool_page_loads(): void
+ {
+ $response = $this->get('/tools/jwt');
+
+ $response->assertStatus(200);
+ $response->assertSee('JWT Decoder');
+ $response->assertSee('Decode and inspect JSON Web Tokens');
+ }
+
+ public function test_jwt_tool_has_required_elements(): void
+ {
+ $response = $this->get('/tools/jwt');
+
+ $response->assertStatus(200);
+ $response->assertSee('JWT Token');
+ $response->assertSee('Header');
+ $response->assertSee('Payload');
+ $response->assertSee('Load sample');
+ }
+
+ public function test_timestamp_tool_page_loads(): void
+ {
+ $response = $this->get('/tools/timestamp');
+
+ $response->assertStatus(200);
+ $response->assertSee('Timestamp Converter');
+ $response->assertSee('Convert Unix timestamps to dates');
+ }
+
+ public function test_timestamp_tool_has_required_elements(): void
+ {
+ $response = $this->get('/tools/timestamp');
+
+ $response->assertStatus(200);
+ $response->assertSee('Timestamp to Date');
+ $response->assertSee('Date to Timestamp');
+ $response->assertSee('Current Unix Timestamp');
+ }
+
+ public function test_diff_tool_page_loads(): void
+ {
+ $response = $this->get('/tools/diff');
+
+ $response->assertStatus(200);
+ $response->assertSee('Diff Checker');
+ $response->assertSee('Compare two texts and highlight differences');
+ }
+
+ public function test_diff_tool_has_required_elements(): void
+ {
+ $response = $this->get('/tools/diff');
+
+ $response->assertStatus(200);
+ $response->assertSee('Original Text');
+ $response->assertSee('Modified Text');
+ $response->assertSee('Compare');
+ }
+
public function test_all_pages_have_navigation(): void
{
- $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron'];
+ $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff'];
foreach ($pages as $page) {
$response = $this->get($page);
@@ -472,7 +530,7 @@ public function test_all_pages_have_navigation(): void
public function test_all_pages_have_theme_toggle(): void
{
- $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron'];
+ $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff'];
foreach ($pages as $page) {
$response = $this->get($page);
@@ -485,7 +543,7 @@ public function test_all_pages_have_theme_toggle(): void
public function test_all_pages_load_vite_assets(): void
{
// Code editor uses standalone template without Vite
- $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron'];
+ $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff'];
foreach ($pages as $page) {
$response = $this->get($page);
@@ -498,7 +556,7 @@ public function test_all_pages_load_vite_assets(): void
public function test_all_tool_pages_have_back_link(): void
{
// Code editor uses standalone template with home link instead of back
- $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron'];
+ $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff'];
foreach ($toolPages as $page) {
$response = $this->get($page);
@@ -551,7 +609,7 @@ public function test_api_routes_reject_get_requests(): void
public function test_pages_have_csrf_token(): void
{
- $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron'];
+ $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff'];
foreach ($pages as $page) {
$response = $this->get($page);