diff --git a/app/Helpers/FirstParty/OpenAI/OpenAI.php b/app/Helpers/FirstParty/OpenAI/OpenAI.php index 64e732a..3b56b63 100644 --- a/app/Helpers/FirstParty/OpenAI/OpenAI.php +++ b/app/Helpers/FirstParty/OpenAI/OpenAI.php @@ -14,7 +14,7 @@ public static function getSiteSummary($parent_categories, $user_prompt, $model_m $category_list = implode('|', $parent_categories->pluck('name')->toArray()); - $system_prompt = "Based on the website content containing an AI tool, return a valid JSON containing:\n{\n\"is_ai_tool\":(true|false),\n\"ai_tool_name\":\"(AI Tool Name)\",\n\"is_app_web_both\":\"(app|web|both)\",\n\"tagline\":\"(One line tagline in 6-8 words)\",\n\"summary\": \"(Summary of AI tool in 2-3 parapgraphs, 140-180 words using grade 8 US english)\",\n\"pricing_type\": \"(Free|Free Trial|Freemium|Subscription|Usage Based)\",\n\"main_category\": \"(AI Training|Art|Audio|Avatars|Business|Chatbots|Coaching|Content Generation|Data|Dating|Design|Dev|Education|Emailing|Finance|Gaming|GPTs|Health|Legal|Marketing|Music|Networking|Personal Assistance|Planning|Podcasting|Productivity|Project Management|Prompting|Reporting|Research|Sales|Security|SEO|Shopping|Simulation|Social|Speech|Support|Task|Testing|Training|Translation|UI\/UX|Video|Workflow|Writing)\",\n\"keywords\":[\"(Identify relevant keywords for this AI Tool, 1-2 words each, at least)\"],\n\"qna\":[{\"q\":\"Typical FAQ that readers want to know, up to 5 questions\",\"a\":\"Answer of the question\"}]\n}"; + $system_prompt = "Based on the website content containing an AI tool, return a valid JSON containing:\n{\n\"is_ai_tool\":(true|false),\n\"ai_tool_name\":\"(AI Tool Name)\",\n\"is_app_web_both\":\"(app|web|both)\",\n\"tagline\":\"(One line tagline in 6-8 words)\",\n\"summary\": \"(Summary of AI tool in 2-3 parapgraphs, 200-240 words using grade 8 US english, start with AI tool name)\",\n\"pricing_type\": \"(Free|Free Trial|Freemium|Subscription|Usage Based)\",\n\"main_category\": \"(AI Training|Art|Audio|Avatars|Business|Chatbots|Coaching|Content Generation|Data|Dating|Design|Dev|Education|Emailing|Finance|Gaming|GPTs|Health|Legal|Marketing|Music|Networking|Personal Assistance|Planning|Podcasting|Productivity|Project Management|Prompting|Reporting|Research|Sales|Security|SEO|Shopping|Simulation|Social|Speech|Support|Task|Testing|Training|Translation|UI\/UX|Video|Workflow|Writing)\",\n\"keywords\":[\"(Identify relevant keywords for this AI Tool, 1-2 words each, at least)\"],\n\"qna\":[{\"q\":\"Typical FAQ that readers want to know, up to 5 questions\",\"a\":\"Answer of the question\"}]\n}"; return self::getChatCompletion($user_prompt, $system_prompt, $openai_config, $model_max_tokens, $timeout); } diff --git a/app/Helpers/Global/helpers.php b/app/Helpers/Global/helpers.php index 6666268..34f95f9 100644 --- a/app/Helpers/Global/helpers.php +++ b/app/Helpers/Global/helpers.php @@ -3,3 +3,4 @@ require 'string_helper.php'; require 'geo_helper.php'; require 'proxy_helper.php'; +require 'route_helper.php'; diff --git a/app/Helpers/Global/route_helper.php b/app/Helpers/Global/route_helper.php new file mode 100644 index 0000000..d95002f --- /dev/null +++ b/app/Helpers/Global/route_helper.php @@ -0,0 +1,13 @@ + strtolower(urlencode($query)), + ]); + } +} diff --git a/app/Helpers/Global/string_helper.php b/app/Helpers/Global/string_helper.php index b205091..475cd6a 100644 --- a/app/Helpers/Global/string_helper.php +++ b/app/Helpers/Global/string_helper.php @@ -1,11 +1,42 @@ format('d M Y'); + + } +} + +if (! function_exists('epoch_now_timestamp')) { + function epoch_now_timestamp($multiplier = 1000) + { + return (int) round(microtime(true) * $multiplier); + } +} + +if (! function_exists('add_params_to_url')) { + function add_params_to_url(string $url, array $newParams): string + { + $url = parse_url($url); + parse_str($url['query'] ?? '', $existingParams); + + $newQuery = array_merge($existingParams, $newParams); + + $newUrl = $url['scheme'].'://'.$url['host'].($url['path'] ?? ''); + if ($newQuery) { + $newUrl .= '?'.http_build_query($newQuery); + } + + if (isset($url['fragment'])) { + $newUrl .= '#'.$url['fragment']; + } + + return $newUrl; } } @@ -175,6 +206,34 @@ function str_first_sentence($str) } +if (! function_exists('str_extract_sentences')) { + function str_extract_sentences($str, $count = 1) + { + // Split the string at ., !, or ?, including the punctuation in the result + $sentences = preg_split('/([.!?])\s*/', $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + $extractedSentences = []; + $currentSentence = ''; + + foreach ($sentences as $key => $sentence) { + if ($key % 2 == 0) { + // This is a sentence fragment + $currentSentence = $sentence; + } else { + // This is a punctuation mark + $currentSentence .= $sentence; + $extractedSentences[] = trim($currentSentence); + + if (count($extractedSentences) >= $count) { + break; + } + } + } + + return $extractedSentences; + } +} + if (! function_exists('unslug')) { function unslug($slug, $delimiter = '-') { diff --git a/app/Http/Controllers/Front/FrontDiscoverController.php b/app/Http/Controllers/Front/FrontDiscoverController.php index 1503212..88cbd02 100644 --- a/app/Http/Controllers/Front/FrontDiscoverController.php +++ b/app/Http/Controllers/Front/FrontDiscoverController.php @@ -3,7 +3,9 @@ namespace App\Http\Controllers\Front; use App\Http\Controllers\Controller; +use App\Models\AiTool; use App\Models\Category; +use Artesaos\SEOTools\Facades\SEOMeta; use Artesaos\SEOTools\Facades\SEOTools; use Illuminate\Http\Request; use JsonLd\Context; @@ -36,7 +38,7 @@ public function discover(Request $request, $category_slug = null) SEOTools::opengraph(); SEOTools::jsonLd(); SEOTools::setTitle($category->name.' AI Tools', false); - //SEOTools::setDescription($description); + //SEOTools::setDescription($description); } else { $breadcrumbs = collect([ ['name' => 'Home', 'url' => route('front.home')], @@ -65,6 +67,15 @@ public function discover(Request $request, $category_slug = null) 'itemListElement' => $listItems, ]); - return view('front.discover', compact('breadcrumbs', 'breadcrumb_context', 'category')); + $ai_tools = AiTool::when(! is_null($category), function ($query) use ($category) { + $query->where('category_id', $category->id); + }) + ->orderBy('updated_at', 'DESC')->paginate(6); + + if ($ai_tools->count() <= 0) { + SEOMeta::setRobots('noindex'); + } + + return view('front.discover', compact('breadcrumbs', 'breadcrumb_context', 'category', 'ai_tools')); } } diff --git a/app/Http/Controllers/Front/FrontHomeController.php b/app/Http/Controllers/Front/FrontHomeController.php index 8d0e8a4..fd808f6 100644 --- a/app/Http/Controllers/Front/FrontHomeController.php +++ b/app/Http/Controllers/Front/FrontHomeController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Front; use App\Http\Controllers\Controller; +use App\Models\AiTool; use Artesaos\SEOTools\Facades\SEOMeta; use Artesaos\SEOTools\Facades\SEOTools; use GrahamCampbell\Markdown\Facades\Markdown; @@ -12,7 +13,9 @@ class FrontHomeController extends Controller { public function index(Request $request) { - return view('front.home'); + $latest_ai_tools = AiTool::orderBy('created_at', 'DESC')->take(12)->get(); + + return view('front.home', compact('latest_ai_tools')); } public function terms(Request $request) diff --git a/app/Http/Controllers/Front/FrontSearchController.php b/app/Http/Controllers/Front/FrontSearchController.php index 2bdbb19..c6deff8 100644 --- a/app/Http/Controllers/Front/FrontSearchController.php +++ b/app/Http/Controllers/Front/FrontSearchController.php @@ -2,9 +2,107 @@ namespace App\Http\Controllers\Front; +use App\Helpers\FirstParty\Aictio\Aictio; use App\Http\Controllers\Controller; +use App\Models\SearchEmbedding; +use App\Models\SearchResult; +use Artesaos\SEOTools\Facades\SEOTools; +use Illuminate\Http\Request; +use JsonLd\Context; class FrontSearchController extends Controller { - // + public function search(Request $request) + { + if (is_empty($request->input('query'))) { + return redirect()->back(); + } + + return redirect()->to(get_route_search_result($request->input('query'))); + } + + public function searchResult(Request $request, $query) + { + if ($request->input('page') > 10) { + abort(404); + } + + if (is_empty(trim($query))) { + return abort(404); + } + + $query = trim($query); + + $pagination_results = 10; + + $search_result = SearchResult::where('query', $query) + ->orderBy('id', 'desc') + ->first(); + + $embedding = null; + + if (! is_null($search_result) && ! is_null($search_result->embedding)) { + $embedding = $search_result->embedding; + $search_result->increment('counts'); + } else { + $embedding = Aictio::getVectorEmbedding($query); + + $search_result = new SearchResult; + $search_result->query = $query; + $search_result->counts = 1; + $search_result->embedding = $embedding; + + if ($search_result->save()) { + + } + } + + $results = SearchEmbedding::query() + ->with('ai_tool') + ->selectRaw('DISTINCT ON (ai_tool_id) ai_tool_id, embedding <-> ? AS distance', [$embedding]) + ->orderBy('ai_tool_id') + ->orderByRaw('embedding <-> ? ASC', [$embedding]) + ->whereRaw('embedding <-> ? < 0.65', [$embedding]) + ->where('ai_tool_id', '!=', null) + ->paginate($pagination_results); + + //dd($results->toArray()); + + if ($results->total() < $pagination_results) { + // TODO: Signal Serp Crawling Engine + } + + $query = strtolower(urldecode($query)); + + session()->put('query', $query); + + SEOTools::metatags(); + SEOTools::twitter(); + SEOTools::opengraph(); + SEOTools::jsonLd(); + SEOTools::setTitle(ucwords($query).' AI Tool'); + + $breadcrumbs = collect([ + ['name' => 'Home', 'url' => route('front.home')], + ['name' => 'AI Tool Search', 'url' => null], + ['name' => ucwords($query), 'url' => null], + ]); + + // breadcrumb json ld + $listItems = []; + + foreach ($breadcrumbs as $index => $breadcrumb) { + $listItems[] = [ + 'name' => $breadcrumb['name'], + 'url' => $breadcrumb['url'], + ]; + } + + $breadcrumb_context = Context::create('breadcrumb_list', [ + 'itemListElement' => $listItems, + ]); + + return view('front.search_results', compact('results', 'query', 'breadcrumbs', 'breadcrumb_context')); + + } } diff --git a/app/Http/Controllers/Front/FrontToolController.php b/app/Http/Controllers/Front/FrontToolController.php new file mode 100644 index 0000000..50a324d --- /dev/null +++ b/app/Http/Controllers/Front/FrontToolController.php @@ -0,0 +1,81 @@ +first(); + + if (is_null($ai_tool)) { + return abort(404); + } + + $ai_tool->load('category'); + + $breadcrumbs = collect([ + ['name' => 'Home', 'url' => route('front.home')], + ['name' => 'AI Tools', 'url' => route('front.discover.home')], + ['name' => $ai_tool->category->name, 'url' => route('front.discover.category', ['category_slug' => $ai_tool->category->slug])], + ['name' => $ai_tool->tool_name, 'url' => null], + ]); + + // breadcrumb json ld + $listItems = []; + + foreach ($breadcrumbs as $index => $breadcrumb) { + $listItems[] = [ + 'name' => $breadcrumb['name'], + 'url' => $breadcrumb['url'], + ]; + } + + $breadcrumb_context = Context::create('breadcrumb_list', [ + 'itemListElement' => $listItems, + ]); + + $applicationCategory = ''; + + if ($ai_tool->is_app_web_both == 'both') { + $applicationCategory = 'App & Web Application'; + } else { + $applicationCategory = ucwords($ai_tool->is_app_web_both).' Application'; + } + + $qnaMainEntity = []; + + foreach ($ai_tool->qna as $qna) { + $qnaMainEntity[] = [ + '@type' => 'Question', + 'name' => $qna->q, + 'acceptedAnswer' => [ + '@type' => 'Answer', + 'text' => $qna->a, + ], + ]; + } + + $faqData = [ + 'mainEntity' => $qnaMainEntity, + ]; + + $faq_context = Context::create(FAQPage::class, $faqData); + + SEOTools::metatags(); + SEOTools::twitter()->addImage($ai_tool->screenshot_img); + SEOTools::opengraph()->addImage($ai_tool->screenshot_img); + SEOTools::jsonLd()->addImage($ai_tool->screenshot_img); + + //dd($faq_context); + + return view('front.aitool', compact('ai_tool', 'breadcrumb_context', 'breadcrumbs', 'faq_context')); + } +} diff --git a/app/Jobs/Tasks/GetAIToolScreenshotTask.php b/app/Jobs/Tasks/GetAIToolScreenshotTask.php index a33ac2b..d47fc39 100644 --- a/app/Jobs/Tasks/GetAIToolScreenshotTask.php +++ b/app/Jobs/Tasks/GetAIToolScreenshotTask.php @@ -4,8 +4,6 @@ use App\Helpers\FirstParty\OSSUploader\OSSUploader; use App\Models\AiTool; -use App\Models\BusinessProfile; -use App\Models\SerpUrl; use App\Models\UrlToCrawl; use Exception; use Image; @@ -13,25 +11,21 @@ class GetAIToolScreenshotTask { - public static function handle($url_to_crawl_id, $ai_tool_id) { $url_to_crawl = UrlToCrawl::find($url_to_crawl_id); - if (is_null($url_to_crawl)) - { - return ; + if (is_null($url_to_crawl)) { + return; } $ai_tool = AiTool::find($ai_tool_id); - if (is_null($ai_tool)) - { - return ; + if (is_null($ai_tool)) { + return; } - $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"; - + $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'; $browsershot = Browsershot::url($url_to_crawl->url) ->timeout(30) diff --git a/app/Jobs/Tasks/GetUrlBodyTask.php b/app/Jobs/Tasks/GetUrlBodyTask.php index ef43001..b6b5db2 100644 --- a/app/Jobs/Tasks/GetUrlBodyTask.php +++ b/app/Jobs/Tasks/GetUrlBodyTask.php @@ -23,25 +23,29 @@ public static function handle(int $url_to_crawl_id) return null; } + $enable_proxy = false; + $url_to_crawl->is_crawling = true; $url_to_crawl->save(); $url_to_crawl->refresh(); - try { - $user_agent = config('platform.proxy.user_agent'); + // try { + $user_agent = config('platform.proxy.user_agent'); - $response = Http::withHeaders([ - 'User-Agent' => $user_agent, + $response = Http::withHeaders([ + 'User-Agent' => $user_agent, + ]) + ->withOptions([ + 'proxy' => ($enable_proxy) ? get_smartproxy_rotating_server() : null, + 'timeout' => 10, + 'verify' => false, ]) - ->withOptions([ - 'proxy' => get_smartproxy_rotating_server(), - 'timeout' => 10, - 'verify' => false, - ]) - ->get($url_to_crawl->url); + ->get($url_to_crawl->url); - if ($response->successful()) { - $raw_html = $response->body(); + if ($response->successful()) { + $raw_html = $response->body(); + if ($enable_proxy) + { $cost = calculate_smartproxy_cost(round(strlen($raw_html) / 1024, 2), 'rotating_global'); $service_cost_usage = new ServiceCostUsage; @@ -51,17 +55,19 @@ public static function handle(int $url_to_crawl_id) $service_cost_usage->reference_2 = strval($url_to_crawl_id); $service_cost_usage->output = self::getMarkdownFromHtml($raw_html); $service_cost_usage->save(); - - } else { - $raw_html = null; - $response->throw(); } - } catch (Exception $e) { + + } else { $raw_html = null; - //throw $e; + $response->throw(); } + // } catch (Exception $e) { + // $raw_html = null; + // //throw $e; + // } + if (! is_empty($raw_html)) { $url_to_crawl->output_type = 'markdown'; $url_to_crawl->output = self::getMarkdownFromHtml($raw_html); diff --git a/app/Jobs/Tasks/ParseUrlBodyTask.php b/app/Jobs/Tasks/ParseUrlBodyTask.php index 45605b7..70172af 100644 --- a/app/Jobs/Tasks/ParseUrlBodyTask.php +++ b/app/Jobs/Tasks/ParseUrlBodyTask.php @@ -65,23 +65,28 @@ public static function handle(int $url_to_crawl_id) $ai_tool->url_to_crawl_id = $url_to_crawl->id; } + $ai_tool->external_url = $url_to_crawl->url; + // Tool Name - if ((isset($url_meta_response->output->tool_name)) && (! is_empty($url_meta_response->output->tool_name))) { - $ai_tool->tool_name = $url_meta_response->output->tool_name; + if ((isset($url_meta_response->output->ai_tool_name)) && (! is_empty($url_meta_response->output->ai_tool_name))) { + $ai_tool->tool_name = $url_meta_response->output->ai_tool_name; + $ai_tool->slug = epoch_now_timestamp(1).'-'.str_slug($url_meta_response->output->ai_tool_name); } else { throw new Exception('OpenAI::getSiteSummary failed, no tool name'); } // Is AI Tool - if ((isset($url_meta_response->output->is_ai_tool)) && (! is_null($url_meta_response->output->is_at_tool)) && is_bool($url_meta_response->output->is_ai_tool)) { + if ((isset($url_meta_response->output->is_ai_tool)) && (! is_null($url_meta_response->output->is_ai_tool)) && is_bool($url_meta_response->output->is_ai_tool)) { $ai_tool->is_ai_tool = $url_meta_response->output->is_ai_tool; } else { $ai_tool->is_ai_tool = true; } // Is App/Web/Both - if ((isset($url_meta_response->output->is_app_web_both)) && (is_array($url_meta_response->output->is_app_web_both)) && in_array($url_meta_response->output->is_app_web_both, ['app', 'web', 'both'])) { + if ((isset($url_meta_response->output->is_app_web_both)) && (! is_empty($url_meta_response->output->is_app_web_both)) && in_array($url_meta_response->output->is_app_web_both, ['app', 'web', 'both'])) { $ai_tool->is_app_web_both = $url_meta_response->output->is_app_web_both; + } else { + $ai_tool->is_app_web_both = 'web'; } // Tagline @@ -130,9 +135,8 @@ public static function handle(int $url_to_crawl_id) $query = $ai_tool->tool_name; - if (!is_empty($ai_tool->tagline)) - { - $query .= ": " . $ai_tool->tagline; + if (! is_empty($ai_tool->tagline)) { + $query .= ': '.$ai_tool->tagline; } StoreSearchEmbeddingJob::dispatch( @@ -176,8 +180,7 @@ public static function handle(int $url_to_crawl_id) // Q&A if ((isset($url_meta_response->output->qna)) && (is_array($url_meta_response->output->qna))) { - foreach ($url_meta_response->output->qna as $qna) - { + foreach ($url_meta_response->output->qna as $qna) { $q = $qna->q; $a = $qna->a; @@ -187,7 +190,7 @@ public static function handle(int $url_to_crawl_id) 'qna', $ai_tool->category_id, $ai_tool->id, - ($qna->q . " " . $qna->a) + ($qna->q.' '.$qna->a) ); } } diff --git a/app/JsonLd/FAQPage.php b/app/JsonLd/FAQPage.php new file mode 100644 index 0000000..f40a049 --- /dev/null +++ b/app/JsonLd/FAQPage.php @@ -0,0 +1,17 @@ + [], + ]; + + protected function setMainEntityAttribute($value) + { + return (array) $value; + } +} diff --git a/app/JsonLd/SoftwareApplication.php b/app/JsonLd/SoftwareApplication.php new file mode 100644 index 0000000..0434dd0 --- /dev/null +++ b/app/JsonLd/SoftwareApplication.php @@ -0,0 +1,77 @@ + null, + 'description' => null, + 'url' => null, + 'applicationCategory' => null, + 'screenshot' => ImageObject::class, + ]; + + /** + * Set the name attribute. + * + * @param string $value + * @return string + */ + protected function setNameAttribute($value) + { + return (string) $value; + } + + /** + * Set the description attribute. + * + * @param string $value + * @return string + */ + protected function setDescriptionAttribute($value) + { + return $this->truncate($value, 260); + } + + /** + * Set the url attribute. + * + * @param string $value + * @return string + */ + protected function setUrlAttribute($value) + { + return (string) $value; + } + + /** + * Set the application category attribute. + * + * @param string $value + * @return string + */ + protected function setApplicationCategoryAttribute($value) + { + return (string) $value; + } + + /** + * Set the screenshot attribute. + * + * @param ImageObject|array $value + * @return ImageObject + */ + protected function setScreenshotAttribute($value) + { + return new ImageObject([$value]); + } +} diff --git a/app/Models/AiTool.php b/app/Models/AiTool.php index 1d545c3..e7ce55d 100644 --- a/app/Models/AiTool.php +++ b/app/Models/AiTool.php @@ -7,12 +7,14 @@ namespace App\Models; use Carbon\Carbon; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Storage; /** * Class AiTool - * + * * @property int $id * @property int $category_id * @property int $url_to_crawl_id @@ -27,56 +29,72 @@ * @property string|null $qna * @property Carbon|null $created_at * @property Carbon|null $updated_at - * * @property Category $category * @property UrlToCrawl $url_to_crawl * @property Collection|SearchEmbedding[] $search_embeddings * @property Collection|AiToolKeyword[] $ai_tool_keywords - * - * @package App\Models */ class AiTool extends Model { - protected $table = 'ai_tools'; + protected $table = 'ai_tools'; - protected $casts = [ - 'category_id' => 'int', - 'url_to_crawl_id' => 'int', - 'is_ai_tool' => 'bool', - 'qna' => 'object' - ]; + protected $casts = [ + 'category_id' => 'int', + 'url_to_crawl_id' => 'int', + 'view_count' => 'int', + 'is_ai_tool' => 'bool', + 'qna' => 'object', + ]; - protected $fillable = [ - 'category_id', - 'url_to_crawl_id', - 'screenshot_img', - 'is_ai_tool', - 'tool_name', - 'is_app_web_both', - 'tagline', - 'summary', - 'pricing_type', - 'keyword_string', - 'qna' - ]; + protected $fillable = [ + 'category_id', + 'url_to_crawl_id', + 'screenshot_img', + 'is_ai_tool', + 'tool_name', + 'slug', + 'is_app_web_both', + 'tagline', + 'summary', + 'pricing_type', + 'keyword_string', + 'view_count', + 'qna', + 'external_url + ', + ]; - public function category() - { - return $this->belongsTo(Category::class); - } + protected function screenshotImg(): Attribute + { + return Attribute::make( + get: function ($value = null) { + if (! is_empty($value)) { - public function url_to_crawl() - { - return $this->belongsTo(UrlToCrawl::class); - } + return Storage::disk(config('platform.uploads.ai_tools.screenshot.driver'))->url(config('platform.uploads.ai_tools.screenshot.path').$value); + } - public function search_embeddings() - { - return $this->hasMany(SearchEmbedding::class); - } + return null; + } + ); + } - public function ai_tool_keywords() - { - return $this->hasMany(AiToolKeyword::class); - } + public function category() + { + return $this->belongsTo(Category::class); + } + + public function url_to_crawl() + { + return $this->belongsTo(UrlToCrawl::class); + } + + public function search_embeddings() + { + return $this->hasMany(SearchEmbedding::class); + } + + public function keywords() + { + return $this->hasMany(AiToolKeyword::class); + } } diff --git a/app/Models/SearchResult.php b/app/Models/SearchResult.php new file mode 100644 index 0000000..9588c25 --- /dev/null +++ b/app/Models/SearchResult.php @@ -0,0 +1,41 @@ + Vector::class, + 'indexed_at' => 'datetime', + 'counts' => 'int', + ]; + + protected $fillable = [ + 'query', + 'embedding', + 'indexed_at', + 'counts', + ]; +} diff --git a/app/Models/ServiceCostUsage.php b/app/Models/ServiceCostUsage.php index 920b598..4add750 100644 --- a/app/Models/ServiceCostUsage.php +++ b/app/Models/ServiceCostUsage.php @@ -29,7 +29,7 @@ class ServiceCostUsage extends Model protected $casts = [ 'cost' => 'float', - 'output' => 'binary', + 'output' => 'object', ]; protected $fillable = [ diff --git a/composer.json b/composer.json index cd90ff1..d9ca3c7 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "laravel/tinker": "^2.8", "laravel/ui": "^4.0", "league/flysystem-aws-s3-v3": "^3.0", + "league/html-to-markdown": "^5.1", "masterminds/html5": "^2.8", "mews/purifier": "^3.4", "pfaciana/tiny-html-minifier": "^3.0", diff --git a/composer.lock b/composer.lock index 754a1e9..3ab8ca6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2b41c19d85eb74a7fb7b976437ef4861", + "content-hash": "84f90f6b5010de195dcd0fead0d21daf", "packages": [ { "name": "alaminfirdows/laravel-editorjs", @@ -3516,6 +3516,95 @@ }, "time": "2023-07-08T06:26:07+00:00" }, + { + "name": "league/html-to-markdown", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "^1.1.0", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^8.5 || ^9.2", + "scrutinizer/ocular": "^1.6", + "unleashedtech/php-coding-standard": "^2.7 || ^3.0", + "vimeo/psalm": "^4.22 || ^5.0" + }, + "bin": [ + "bin/html-to-markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "description": "An HTML-to-markdown conversion helper for PHP", + "homepage": "https://github.com/thephpleague/html-to-markdown", + "keywords": [ + "html", + "markdown" + ], + "support": { + "issues": "https://github.com/thephpleague/html-to-markdown/issues", + "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown", + "type": "tidelift" + } + ], + "time": "2023-07-12T21:21:09+00:00" + }, { "name": "league/mime-type-detection", "version": "1.14.0", diff --git a/config/platform/ai.php b/config/platform/ai.php index c30e042..cd35fa8 100644 --- a/config/platform/ai.php +++ b/config/platform/ai.php @@ -6,4 +6,42 @@ 'api_key' => env('OPENAI_API_KEY'), ], + // GPT 4 + 'openai-gpt-4-turbo' => [ + 'tokens' => 128000, + 'model' => 'gpt-4-1106-preview', + 'input_cost_per_thousand_tokens' => 0.01, + 'output_cost_per_thousand_tokens' => 0.03, + ], + + 'openai-gpt-4' => [ + 'tokens' => 8192, + 'model' => 'gpt-4-0613', + 'input_cost_per_thousand_tokens' => 0.03, + 'output_cost_per_thousand_tokens' => 0.06, + ], + + // GPT 3.5 + + 'openai-gpt-3-5-turbo-1106' => [ + 'tokens' => 16385, + 'model' => 'gpt-3.5-turbo-1106', + 'input_cost_per_thousand_tokens' => 0.0010, + 'output_cost_per_thousand_tokens' => 0.002, + ], + + 'openai-3-5-turbo' => [ + 'tokens' => 4096, + 'model' => 'gpt-3.5-turbo', + 'input_cost_per_thousand_tokens' => 0.0015, + 'output_cost_per_thousand_tokens' => 0.002, + ], + + 'openai-3-5-turbo-16k' => [ + 'tokens' => 16385, + 'model' => 'gpt-3.5-turbo-16k', + 'input_cost_per_thousand_tokens' => 0.003, + 'output_cost_per_thousand_tokens' => 0.004, + ], + ]; diff --git a/config/seotools.php b/config/seotools.php index 13b60a5..d95376d 100644 --- a/config/seotools.php +++ b/config/seotools.php @@ -11,9 +11,12 @@ 'defaults' => [ 'title' => 'AI Buddy Tools', // set false to total remove 'titleBefore' => false, // Put defaults.title before page title, like 'It's Over 9000! - Dashboard' - 'description' => 'The leading AI tools directory and search engine, Discover the best AI tools, apps and services list and take your business to the next level.', // set false to total remove + 'description' => 'Your friendly AI tool directory platform. Discover AI Tools for any task!', // set false to total remove 'separator' => ' - ', - 'keywords' => [], + 'keywords' => [ + 'ai tools directory', + 'yellow page ai tools', + ], 'canonical' => 'full', // Set to null or 'full' to use Url::full(), set to 'current' to use Url::current(), set false to total remove 'robots' => false, // Set to 'all', 'none' or any combination of index/noindex and follow/nofollow ], @@ -36,12 +39,16 @@ * The default configurations to be used by the opengraph generator. */ 'defaults' => [ - 'title' => 'AI Buddy Tools', // set false to total remove - 'description' => 'The leading AI tools directory and search engine, Discover the best AI tools, apps and services list and take your business to the next level.', // set false to total remove - 'url' => false, // Set null for using Url::current(), set false to total remove + 'title' => 'AI Buddy Tool', // set false to total remove + 'description' => 'Your friendly AI tool directory platform. Discover AI Tools for any task!', // set false to total remove + 'url' => null, // Set null for using Url::current(), set false to total remove 'type' => false, - 'site_name' => false, - 'images' => [], + 'site_name' => 'AI Buddy Tool', + 'images' => [ + 'https://cdn.aibuddytool.com/aibuddytool-og.webp', + 'https://cdn.aibuddytool.com/aibuddytool-og.png', + 'https://cdn.aibuddytool.com/aibuddytool-og.jpg' + ], ], ], 'twitter' => [ @@ -58,11 +65,15 @@ * The default configurations to be used by the json-ld generator. */ 'defaults' => [ - 'title' => 'AI Buddy Tools', // set false to total remove - 'description' => 'The leading AI tools directory and search engine, Discover the best AI tools, apps and services list and take your business to the next level.', // set false to total remove - 'url' => false, // Set to null or 'full' to use Url::full(), set to 'current' to use Url::current(), set false to total remove + 'title' => 'AI Buddy Tool', // set false to total remove + 'description' => 'Your friendly AI tool directory platform. Discover AI Tools for any task!', // set false to total remove + 'url' => 'current', // Set to null or 'full' to use Url::full(), set to 'current' to use Url::current(), set false to total remove 'type' => 'WebPage', - 'images' => [], + 'images' => [ + 'https://cdn.aibuddytool.com/aibuddytool-og.webp', + 'https://cdn.aibuddytool.com/aibuddytool-og.png', + 'https://cdn.aibuddytool.com/aibuddytool-og.jpg' + ], ], ], ]; diff --git a/database/migrations/2023_11_26_071759_create_ai_tools_table.php b/database/migrations/2023_11_26_071759_create_ai_tools_table.php index 37eafe8..72b9a81 100644 --- a/database/migrations/2023_11_26_071759_create_ai_tools_table.php +++ b/database/migrations/2023_11_26_071759_create_ai_tools_table.php @@ -17,10 +17,14 @@ public function up(): void $table->foreignId('category_id'); $table->foreignId('url_to_crawl_id'); + $table->string('view_count')->default(0); + $table->string('screenshot_img')->nullable(); $table->boolean('is_ai_tool')->default(true); $table->string('tool_name'); + $table->string('slug'); + $table->string('external_url'); $table->string('is_app_web_both'); $table->text('tagline')->nullable(); @@ -32,6 +36,9 @@ public function up(): void $table->foreign('category_id')->references('id')->on('categories'); $table->foreign('url_to_crawl_id')->references('id')->on('url_to_crawls'); + + $table->index('view_count'); + $table->index('slug'); }); } diff --git a/database/migrations/2023_11_26_072107_create_ai_tool_keywords_table.php b/database/migrations/2023_11_26_072107_create_ai_tool_keywords_table.php index c048b66..9b11090 100644 --- a/database/migrations/2023_11_26_072107_create_ai_tool_keywords_table.php +++ b/database/migrations/2023_11_26_072107_create_ai_tool_keywords_table.php @@ -24,6 +24,8 @@ public function up(): void $table->foreign('category_id')->references('id')->on('categories'); $table->foreign('ai_tool_id')->references('id')->on('ai_tools'); + + $table->index('ai_tool_id'); }); } diff --git a/database/migrations/2023_11_26_072145_create_search_embeddings_table.php b/database/migrations/2023_11_26_072145_create_search_embeddings_table.php index 9bea264..e57c3be 100644 --- a/database/migrations/2023_11_26_072145_create_search_embeddings_table.php +++ b/database/migrations/2023_11_26_072145_create_search_embeddings_table.php @@ -2,6 +2,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class extends Migration @@ -25,7 +26,11 @@ public function up(): void $table->foreign('ai_tool_id')->references('id')->on('ai_tools'); $table->foreign('category_id')->references('id')->on('categories'); + }); + + DB::statement('CREATE INDEX search_embeddings_embedding_idx ON search_embeddings USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)'); + } /** diff --git a/database/migrations/2023_11_27_034006_create_search_results_table.php b/database/migrations/2023_11_27_034006_create_search_results_table.php new file mode 100644 index 0000000..cf5e277 --- /dev/null +++ b/database/migrations/2023_11_27_034006_create_search_results_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('query'); + $table->vector('embedding', 384)->nullable(); + $table->datetime('indexed_at')->nullable(); + $table->bigInteger('counts')->default(1); + $table->timestamps(); + }); + + DB::statement('CREATE INDEX search_results_embedding_idx ON search_results USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)'); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('search_results'); + } +}; diff --git a/database/seeders/ParentCategorySeeder.php b/database/seeders/ParentCategorySeeder.php index fbdc988..3122318 100644 --- a/database/seeders/ParentCategorySeeder.php +++ b/database/seeders/ParentCategorySeeder.php @@ -45,8 +45,8 @@ public function run(): void ['name' => 'Content Generation', 'emoji' => '📝', 'is_top' => true], ['name' => 'Support', 'emoji' => '🛠️', 'is_top' => false], ['name' => 'Health', 'emoji' => '🍏', 'is_top' => false], - ['name' => 'Networking', 'emoji' => '🔗', 'is_top' => false], - ['name' => 'Personal Assistance', 'emoji' => '🤝', 'is_top' => false], + ['name' => 'Networking', 'emoji' => '🤝', 'is_top' => false], + ['name' => 'Personal Assistance', 'emoji' => '📱', 'is_top' => false], ['name' => 'Planning', 'emoji' => '📅', 'is_top' => false], ['name' => 'Project Management', 'emoji' => '📈', 'is_top' => false], ['name' => 'Reporting', 'emoji' => '📑', 'is_top' => false], diff --git a/public/ai-buddy-tool-logo-1024x1024.png b/public/ai-buddy-tool-logo-1024x1024.png new file mode 100644 index 0000000..7adaef7 Binary files /dev/null and b/public/ai-buddy-tool-logo-1024x1024.png differ diff --git a/public/ai-buddy-tool-logo-1024x1024.svg b/public/ai-buddy-tool-logo-1024x1024.svg new file mode 100644 index 0000000..d1a2cfb --- /dev/null +++ b/public/ai-buddy-tool-logo-1024x1024.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/ai-buddy-tool-logo-512x512.png b/public/ai-buddy-tool-logo-512x512.png new file mode 100644 index 0000000..ab0137d Binary files /dev/null and b/public/ai-buddy-tool-logo-512x512.png differ diff --git a/public/ai-buddy-tool-logo-512x512.svg b/public/ai-buddy-tool-logo-512x512.svg new file mode 100644 index 0000000..8187557 --- /dev/null +++ b/public/ai-buddy-tool-logo-512x512.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/aibuddytool-og.jpg b/public/aibuddytool-og.jpg new file mode 100644 index 0000000..be51e78 Binary files /dev/null and b/public/aibuddytool-og.jpg differ diff --git a/public/aibuddytool-og.png b/public/aibuddytool-og.png new file mode 100644 index 0000000..380f436 Binary files /dev/null and b/public/aibuddytool-og.png differ diff --git a/public/aibuddytool-og.webp b/public/aibuddytool-og.webp new file mode 100644 index 0000000..4e5c25d Binary files /dev/null and b/public/aibuddytool-og.webp differ diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png index 685d5e5..bd3f1a1 100644 Binary files a/public/android-chrome-192x192.png and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png index fde46e8..3ccf4db 100644 Binary files a/public/android-chrome-512x512.png and b/public/android-chrome-512x512.png differ diff --git a/public/android-chrome-maskable-192x192.png b/public/android-chrome-maskable-192x192.png new file mode 100644 index 0000000..53f5498 Binary files /dev/null and b/public/android-chrome-maskable-192x192.png differ diff --git a/public/android-chrome-maskable-512x512.png b/public/android-chrome-maskable-512x512.png new file mode 100644 index 0000000..3ccf4db Binary files /dev/null and b/public/android-chrome-maskable-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index 2c385ca..4a8d88d 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png deleted file mode 100644 index a8154e3..0000000 Binary files a/public/favicon-16x16.png and /dev/null differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png deleted file mode 100644 index b00018b..0000000 Binary files a/public/favicon-32x32.png and /dev/null differ diff --git a/public/favicon.ico b/public/favicon.ico index 78e4bd0..a91b12e 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/featured-on-aibuddytool-1-1000.png b/public/featured-on-aibuddytool-1-1000.png new file mode 100644 index 0000000..2f51fca Binary files /dev/null and b/public/featured-on-aibuddytool-1-1000.png differ diff --git a/public/featured-on-aibuddytool-1-1000.webp b/public/featured-on-aibuddytool-1-1000.webp new file mode 100644 index 0000000..aa688df Binary files /dev/null and b/public/featured-on-aibuddytool-1-1000.webp differ diff --git a/public/icon-128x128.png b/public/icon-128x128.png deleted file mode 100644 index 866f153..0000000 Binary files a/public/icon-128x128.png and /dev/null differ diff --git a/public/icon-144x144.png b/public/icon-144x144.png deleted file mode 100644 index 14b12ed..0000000 Binary files a/public/icon-144x144.png and /dev/null differ diff --git a/public/icon-152x152.png b/public/icon-152x152.png deleted file mode 100644 index 6096a40..0000000 Binary files a/public/icon-152x152.png and /dev/null differ diff --git a/public/icon-192x192.png b/public/icon-192x192.png deleted file mode 100644 index 7e6524f..0000000 Binary files a/public/icon-192x192.png and /dev/null differ diff --git a/public/icon-384x384.png b/public/icon-384x384.png deleted file mode 100644 index 456550d..0000000 Binary files a/public/icon-384x384.png and /dev/null differ diff --git a/public/icon-48x48.png b/public/icon-48x48.png deleted file mode 100644 index e17462f..0000000 Binary files a/public/icon-48x48.png and /dev/null differ diff --git a/public/icon-512x512.png b/public/icon-512x512.png deleted file mode 100644 index 801fd81..0000000 Binary files a/public/icon-512x512.png and /dev/null differ diff --git a/public/icon-570x570.png b/public/icon-570x570.png deleted file mode 100644 index 75e8c5e..0000000 Binary files a/public/icon-570x570.png and /dev/null differ diff --git a/public/icon-72x72.png b/public/icon-72x72.png deleted file mode 100644 index 66df1fb..0000000 Binary files a/public/icon-72x72.png and /dev/null differ diff --git a/public/icon-96x96.png b/public/icon-96x96.png deleted file mode 100644 index 2249643..0000000 Binary files a/public/icon-96x96.png and /dev/null differ diff --git a/public/site.webmanifest b/public/site.webmanifest index 45dc8a2..5289599 100644 --- a/public/site.webmanifest +++ b/public/site.webmanifest @@ -1 +1 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{"name":"AI Buddy Tool","short_name":"AI Buddy","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/resources/js/FrontApp.vue b/resources/js/FrontApp.vue new file mode 100644 index 0000000..6b4f436 --- /dev/null +++ b/resources/js/FrontApp.vue @@ -0,0 +1,9 @@ + + + diff --git a/resources/js/app-front.js b/resources/js/app-front.js index 93349a7..7ea68be 100644 --- a/resources/js/app-front.js +++ b/resources/js/app-front.js @@ -1 +1,57 @@ -import * as bootstrap from "~bootstrap"; +import "@tabler/core/src/js/tabler.js"; + +import "./bootstrap"; + +import { createApp, defineAsyncComponent } from "vue"; + +import FrontApp from "@/FrontApp.vue"; + +const app = createApp({ FrontApp }); +const vueComponents = import.meta.glob("@/vue/**/*.vue", { + eager: false, +}); + +console.log(vueComponents); + +import.meta.glob(["../images/**", "../fonts/**"]); + +import { createPinia } from "pinia"; +app.use(createPinia()); + +import axios from "./plugins/axios"; +import VueAxios from "vue-axios"; +app.use(VueAxios, axios); + +import auth from "./plugins/auth"; +app.use(auth); + +import eventBus from "./plugins/event-bus"; +app.use(eventBus); + +import Vue3Toastify from "vue3-toastify"; +import "../css/toastify.css"; +app.use(Vue3Toastify); + +import { Ziggy as ZiggyRoute } from "./ziggy"; +import { ZiggyVue } from "ziggy-js/dist/vue"; +app.use(ZiggyVue, ZiggyRoute); + +window.Ziggy = ZiggyRoute; + +Object.entries({ ...vueComponents }).forEach(([path, definition]) => { + // Get name of component, based on filename + // "./components/Fruits.vue" will become "Fruits" + const componentName = path + .split("/") + .pop() + .replace(/\.\w+$/, "") + .replace(/([a-z])([A-Z])/g, "$1-$2") + .toLowerCase(); + // console.log(componentName); + // console.log(typeof definition); + + // Register component on this Vue instance + app.component(componentName, defineAsyncComponent(definition)); +}); + +app.mount("#app"); diff --git a/resources/js/vue/GetEmbedCode.vue b/resources/js/vue/GetEmbedCode.vue new file mode 100644 index 0000000..55ed7bf --- /dev/null +++ b/resources/js/vue/GetEmbedCode.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/resources/js/ziggy.js b/resources/js/ziggy.js index 0ea6493..d1dcae0 100644 --- a/resources/js/ziggy.js +++ b/resources/js/ziggy.js @@ -1,4 +1,4 @@ -const Ziggy = {"url":"https:\/\/aibuddytool.test","port":null,"defaults":{},"routes":{"debugbar.openhandler":{"uri":"_debugbar\/open","methods":["GET","HEAD"]},"debugbar.clockwork":{"uri":"_debugbar\/clockwork\/{id}","methods":["GET","HEAD"]},"debugbar.assets.css":{"uri":"_debugbar\/assets\/stylesheets","methods":["GET","HEAD"]},"debugbar.assets.js":{"uri":"_debugbar\/assets\/javascript","methods":["GET","HEAD"]},"debugbar.cache.delete":{"uri":"_debugbar\/cache\/{key}\/{tags?}","methods":["DELETE"]},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"ignition.healthCheck":{"uri":"_ignition\/health-check","methods":["GET","HEAD"]},"ignition.executeSolution":{"uri":"_ignition\/execute-solution","methods":["POST"]},"ignition.updateConfig":{"uri":"_ignition\/update-config","methods":["POST"]},"api.auth.login.post":{"uri":"api\/login","methods":["POST"]},"api.auth.logout.post":{"uri":"api\/logout","methods":["POST"]},"api.admin.post.get":{"uri":"api\/admin\/post\/{id}","methods":["GET","HEAD"]},"api.admin.country-locales":{"uri":"api\/admin\/country-locales","methods":["GET","HEAD"]},"api.admin.categories":{"uri":"api\/admin\/categories\/{country_locale_slug}","methods":["GET","HEAD"]},"api.admin.authors":{"uri":"api\/admin\/authors","methods":["GET","HEAD"]},"api.admin.upload.cloud.image":{"uri":"api\/admin\/image\/upload","methods":["POST"]},"api.admin.post.upsert":{"uri":"api\/admin\/admin\/post\/upsert","methods":["POST"]},"feeds.main":{"uri":"posts.rss","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"logout":{"uri":"logout","methods":["POST"]},"register":{"uri":"register","methods":["GET","HEAD"]},"password.request":{"uri":"password\/reset","methods":["GET","HEAD"]},"password.email":{"uri":"password\/email","methods":["POST"]},"password.reset":{"uri":"password\/reset\/{token}","methods":["GET","HEAD"]},"password.update":{"uri":"password\/reset","methods":["POST"]},"password.confirm":{"uri":"password\/confirm","methods":["GET","HEAD"]},"dashboard":{"uri":"admin","methods":["GET","HEAD"]},"admin.changelog":{"uri":"admin\/changelog","methods":["GET","HEAD"]},"about":{"uri":"admin\/about","methods":["GET","HEAD"]},"users.index":{"uri":"admin\/users","methods":["GET","HEAD"]},"posts.manage":{"uri":"admin\/posts","methods":["GET","HEAD"]},"posts.manage.edit":{"uri":"admin\/posts\/edit\/{post_id}","methods":["GET","HEAD"]},"posts.manage.delete":{"uri":"admin\/posts\/delete\/{post_id}","methods":["GET","HEAD"]},"posts.manage.indexing":{"uri":"admin\/posts\/indexing\/{post_id}","methods":["GET","HEAD"]},"posts.manage.new":{"uri":"admin\/posts\/new","methods":["GET","HEAD"]},"profile.show":{"uri":"admin\/profile","methods":["GET","HEAD"]},"profile.update":{"uri":"admin\/profile","methods":["PUT"]},"front.home":{"uri":"\/","methods":["GET","HEAD"]},"front.discover.home":{"uri":"discover","methods":["GET","HEAD"]},"front.discover.category":{"uri":"discover\/{category_slug}","methods":["GET","HEAD"]},"front.terms":{"uri":"terms","methods":["GET","HEAD"]},"front.privacy":{"uri":"privacy","methods":["GET","HEAD"]},"front.disclaimer":{"uri":"disclaimer","methods":["GET","HEAD"]}}}; +const Ziggy = {"url":"https:\/\/aibuddytool.test","port":null,"defaults":{},"routes":{"debugbar.openhandler":{"uri":"_debugbar\/open","methods":["GET","HEAD"]},"debugbar.clockwork":{"uri":"_debugbar\/clockwork\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"debugbar.assets.css":{"uri":"_debugbar\/assets\/stylesheets","methods":["GET","HEAD"]},"debugbar.assets.js":{"uri":"_debugbar\/assets\/javascript","methods":["GET","HEAD"]},"debugbar.cache.delete":{"uri":"_debugbar\/cache\/{key}\/{tags?}","methods":["DELETE"],"parameters":["key","tags"]},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"ignition.healthCheck":{"uri":"_ignition\/health-check","methods":["GET","HEAD"]},"ignition.executeSolution":{"uri":"_ignition\/execute-solution","methods":["POST"]},"ignition.updateConfig":{"uri":"_ignition\/update-config","methods":["POST"]},"api.auth.login.post":{"uri":"api\/login","methods":["POST"]},"api.auth.logout.post":{"uri":"api\/logout","methods":["POST"]},"api.admin.post.get":{"uri":"api\/admin\/post\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"api.admin.country-locales":{"uri":"api\/admin\/country-locales","methods":["GET","HEAD"]},"api.admin.categories":{"uri":"api\/admin\/categories\/{country_locale_slug}","methods":["GET","HEAD"],"parameters":["country_locale_slug"]},"api.admin.authors":{"uri":"api\/admin\/authors","methods":["GET","HEAD"]},"api.admin.upload.cloud.image":{"uri":"api\/admin\/image\/upload","methods":["POST"]},"api.admin.post.upsert":{"uri":"api\/admin\/admin\/post\/upsert","methods":["POST"]},"feeds.main":{"uri":"posts.rss","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"logout":{"uri":"logout","methods":["POST"]},"register":{"uri":"register","methods":["GET","HEAD"]},"password.request":{"uri":"password\/reset","methods":["GET","HEAD"]},"password.email":{"uri":"password\/email","methods":["POST"]},"password.reset":{"uri":"password\/reset\/{token}","methods":["GET","HEAD"],"parameters":["token"]},"password.update":{"uri":"password\/reset","methods":["POST"]},"password.confirm":{"uri":"password\/confirm","methods":["GET","HEAD"]},"dashboard":{"uri":"admin","methods":["GET","HEAD"]},"admin.changelog":{"uri":"admin\/changelog","methods":["GET","HEAD"]},"about":{"uri":"admin\/about","methods":["GET","HEAD"]},"users.index":{"uri":"admin\/users","methods":["GET","HEAD"]},"posts.manage":{"uri":"admin\/posts","methods":["GET","HEAD"]},"posts.manage.edit":{"uri":"admin\/posts\/edit\/{post_id}","methods":["GET","HEAD"],"parameters":["post_id"]},"posts.manage.delete":{"uri":"admin\/posts\/delete\/{post_id}","methods":["GET","HEAD"],"parameters":["post_id"]},"posts.manage.indexing":{"uri":"admin\/posts\/indexing\/{post_id}","methods":["GET","HEAD"],"parameters":["post_id"]},"posts.manage.new":{"uri":"admin\/posts\/new","methods":["GET","HEAD"]},"profile.show":{"uri":"admin\/profile","methods":["GET","HEAD"]},"profile.update":{"uri":"admin\/profile","methods":["PUT"]},"front.home":{"uri":"\/","methods":["GET","HEAD"]},"front.discover.home":{"uri":"discover","methods":["GET","HEAD"]},"front.discover.category":{"uri":"discover\/{category_slug}","methods":["GET","HEAD"],"parameters":["category_slug"]},"front.search.post":{"uri":"ai-search","methods":["POST"]},"front.search.results":{"uri":"ai-search\/{query}","methods":["GET","HEAD"],"parameters":["query"]},"front.aitool.show":{"uri":"ai-tool\/{ai_tool_slug}","methods":["GET","HEAD"],"parameters":["ai_tool_slug"]},"front.terms":{"uri":"terms","methods":["GET","HEAD"]},"front.privacy":{"uri":"privacy","methods":["GET","HEAD"]},"front.disclaimer":{"uri":"disclaimer","methods":["GET","HEAD"]}}}; if (typeof window !== 'undefined' && typeof window.Ziggy !== 'undefined') { Object.assign(Ziggy.routes, window.Ziggy.routes); diff --git a/resources/markdown/disclaimer.md b/resources/markdown/disclaimer.md index ed86f26..3cc58aa 100644 --- a/resources/markdown/disclaimer.md +++ b/resources/markdown/disclaimer.md @@ -26,4 +26,4 @@ ## **5. Agreement** ## **6. Contact** -If you have any questions regarding this disclaimer, contact us via email: support@aibuddytool.com +If you have any questions regarding this disclaimer, contact us via email: contact@aibuddytool.com diff --git a/resources/markdown/privacy.md b/resources/markdown/privacy.md index a8eadb9..bf43a17 100644 --- a/resources/markdown/privacy.md +++ b/resources/markdown/privacy.md @@ -238,4 +238,4 @@ ## Contact Us Don't hesitate to contact us if you have any questions. --Via Email: support@aibuddytool.com +-Via Email: contact@aibuddytool.com diff --git a/resources/markdown/terms.md b/resources/markdown/terms.md index 2f33975..013c74c 100644 --- a/resources/markdown/terms.md +++ b/resources/markdown/terms.md @@ -160,7 +160,7 @@ ## Agreement to Arbitrate ## Notice of Dispute -In the event of a dispute, you or aibuddytool.com must give the other a Notice of Dispute, which is a written statement that sets forth the name, address, and contact information of the party giving it, the facts giving rise to the dispute, and the relief requested. You must send any Notice of Dispute via email to: support@aibuddytool.com. aibuddytool.com will send any Notice of Dispute to you by mail to your address if we have it, or otherwise to your email address. You and aibuddytool.com will attempt to resolve any dispute through informal negotiation within sixty (60) days from the date the Notice of Dispute is sent. After sixty (60) days, you or aibuddytool.com may commence arbitration. +In the event of a dispute, you or aibuddytool.com must give the other a Notice of Dispute, which is a written statement that sets forth the name, address, and contact information of the party giving it, the facts giving rise to the dispute, and the relief requested. You must send any Notice of Dispute via email to: contact@aibuddytool.com. aibuddytool.com will send any Notice of Dispute to you by mail to your address if we have it, or otherwise to your email address. You and aibuddytool.com will attempt to resolve any dispute through informal negotiation within sixty (60) days from the date the Notice of Dispute is sent. After sixty (60) days, you or aibuddytool.com may commence arbitration. ## Binding Arbitration @@ -198,4 +198,4 @@ ## Contact Us Don't hesitate to contact us if you have any questions. -- Via Email: support@aibuddytool.com +- Via Email: contact@aibuddytool.com diff --git a/resources/sass/_variables.scss b/resources/sass/_variables.scss index 537aab4..7e071f8 100644 --- a/resources/sass/_variables.scss +++ b/resources/sass/_variables.scss @@ -52,7 +52,7 @@ $green: #198754; $teal: #20c997; $cyan: #0dcaf0; $highlighter-yellow: #ccf62b; -$highlighter-pink: #ff90ea; +$highlighter-pink: #feacf5; $highlighter-orange: #ff962a; $highlighter-blue: #507fff; $highlighter-purple: #952fff; @@ -314,7 +314,7 @@ $cyans: ( // fusv-enable // scss-docs-start theme-color-variables -$primary: $highlighter-blue; +$primary: $highlighter-purple; $secondary: $gray-600; $success: $green; $info: $cyan; @@ -347,4 +347,3 @@ $highlighter-colors: ( ); $theme-colors: map-merge($theme-colors, $highlighter-colors); -$link-color: $highlighter-purple; diff --git a/resources/views/front/aitool.blade.php b/resources/views/front/aitool.blade.php new file mode 100644 index 0000000..5d107c1 --- /dev/null +++ b/resources/views/front/aitool.blade.php @@ -0,0 +1,160 @@ +@extends('front.layouts.app') + +@section('content') +
+
+ @include('front.partials.breadcrumbs') +

{{ $ai_tool->tool_name }}

+

{{ $ai_tool->tagline }}

+
+
+
+
+ +
+
+ + @if (!is_empty($ai_tool->screenshot_img)) +
+
+ Screenshot of {{ $ai_tool->tool_name }} website +
Screenshot of {{ $ai_tool->tool_name }} ({{ get_domain_from_url($ai_tool->external_url) }}) website +
+
+ +
+ @endif + +
+ +
+
+ +
This AI tool, {{ $ai_tool->tool_name }} + was first featured in AiBuddyTool.com + on {{ dmy($ai_tool->created_at) }}.
+ + + + +
+
+
+
+ + +
+
+

{{ $ai_tool->tool_name }} summary by AIToolBuddy

+
{!! $ai_tool->summary !!}
+
+ +

Details

+ + + + + + + + + + + +
Pricing Structure{{ $ai_tool->pricing_type }}
AI Tool Platform + @if ($ai_tool->is_app_web_both == 'both') + App & Web + @else + {{ ucwords($ai_tool->is_app_web_both) }} Only + @endif +
+
+

{{ $ai_tool->tool_name }} Features

+
+ @foreach ($ai_tool->keywords as $keyword) + {{ $keyword->value }} + + @endforeach +
+
+
+ + @if (count($ai_tool->qna) > 0) +
+
+
+

Frequently Asked Questions about + {{ $ai_tool->tool_name }}

+ +
+ + @foreach ($ai_tool->qna as $key => $faq) +
+

+ +

+
+
+ {!! $faq->a !!} +
+
+
+ @endforeach + +
+
+
+
+ @endif +
+ +
+ +
+ +@endsection + +@push('top_head') + {!! $faq_context !!} +@endpush diff --git a/resources/views/front/discover.blade.php b/resources/views/front/discover.blade.php index e78a4ca..1e8ea95 100644 --- a/resources/views/front/discover.blade.php +++ b/resources/views/front/discover.blade.php @@ -10,6 +10,12 @@ Discover over {{ $tools_count }} AI Tools Today @endif +
+ +
+ @include('front.partials.search') +
+
@@ -19,15 +25,20 @@

- -
- @for ($i = 1; $i <= 18; $i++) -
- @include('front.partials.ai-tool-card') + @if ($ai_tools->count() > 0) +
+ @foreach ($ai_tools as $ai_tool) +
+ @include('front.partials.ai-tool-card') +
+ @endforeach +
+ @else +
+
+

😱 Yikes!

+

This is embarassing, there are no AI tools in this category yet.
We will find + more AI to add here. In the meantime:

+ See more AI Tools +
- @endfor +
+ @endif + +
+ @if ($ai_tools instanceof \Illuminate\Pagination\LengthAwarePaginator) + @if ($ai_tools->hasPages()) +
+ {{ $ai_tools->onEachSide(1)->links('pagination::bootstrap-5-pagination-limit-10') }} +
+ @endif + @elseif ($ai_tools instanceof \Illuminate\Pagination\CursorPaginator) + @if ($ai_tools->hasPages()) +
+ {{ $ai_tools->links('pagination::simple-bootstrap-5') }} +
+ @endif + @endif
+
+ Use Search to discover AI tools by brand, task or features. +
+ +
@endsection diff --git a/resources/views/front/discover_category.blade.php b/resources/views/front/discover_category.blade.php deleted file mode 100644 index c8e1d2a..0000000 --- a/resources/views/front/discover_category.blade.php +++ /dev/null @@ -1,7 +0,0 @@ -@extends('front.layouts.app') - -@section('content') -

- Discover Art AI Tools Today -

-@endsection diff --git a/resources/views/front/home.blade.php b/resources/views/front/home.blade.php index 8239f69..a710e5f 100644 --- a/resources/views/front/home.blade.php +++ b/resources/views/front/home.blade.php @@ -8,42 +8,43 @@ class="bg-highlighter-pink fw-bold px-2">Any Task

Curating over 750+ AI tools and growing 🛠️

-
+
+
+ @include('front.partials.search') +
-
- - -
-
- -
- Filter by - @foreach ($top_parent_categories as $category) - {{ $category->name }} - {{ $category->emoji }} - @endforeach - and - {{ $non_top_parent_categories->count() }} more top categories! -
- - -
- - -
-

Featured AI Tools

-
- @for ($i = 1; $i <= 18; $i++) -
- @include('front.partials.ai-tool-card') +
+
+ Filter by + @foreach ($top_parent_categories as $category) + {{ $category->name }} + {{ $category->emoji }} + @endforeach + and + {{ $non_top_parent_categories->count() }} more top categories!
- @endfor + +
+
+
+ + + @if ($latest_ai_tools->count() > 0) +
+

Featured AI Tools

+
+ @foreach ($latest_ai_tools as $ai_tool) +
+ @include('front.partials.ai-tool-card', ['ai_tool' => $ai_tool]) +
+ @endforeach +
+
+ @endif
Discover {{ $tools_count }} More AI Tools diff --git a/resources/views/front/layouts/app.blade.php b/resources/views/front/layouts/app.blade.php index 0a99e11..c51262b 100644 --- a/resources/views/front/layouts/app.blade.php +++ b/resources/views/front/layouts/app.blade.php @@ -13,15 +13,14 @@ - - + - - @vite('resources/sass/app-front.scss') + @stack('top_head') + @yield('custom_styles') @@ -31,19 +30,23 @@ - @include('googletagmanager::body') +
- @include('front.layouts.navigation') + @include('googletagmanager::body') - @yield('content') + @include('front.layouts.navigation') - @include('front.layouts.footer') + @yield('content') - - @vite('resources/js/app-front.js') + @include('front.layouts.footer') - - @yield('custom_scripts') + + @vite('resources/js/app-front.js') + + + @yield('custom_scripts') + +
diff --git a/resources/views/front/layouts/navigation.blade.php b/resources/views/front/layouts/navigation.blade.php index 64ab789..b5da3c8 100644 --- a/resources/views/front/layouts/navigation.blade.php +++ b/resources/views/front/layouts/navigation.blade.php @@ -1,9 +1,10 @@
-
+
- -

+ + AIBuddyTool.com Logo +

{{ config('app.name') }}

diff --git a/resources/views/front/partials/ai-tool-card.blade.php b/resources/views/front/partials/ai-tool-card.blade.php index f1ee826..3fa982c 100644 --- a/resources/views/front/partials/ai-tool-card.blade.php +++ b/resources/views/front/partials/ai-tool-card.blade.php @@ -1,22 +1,48 @@ -
-
- - +
+
- -

Yada {{ $i }}

+
+

{{ $ai_tool->tool_name }}

-

Rewin.ai is an AI tool that helps content creators generate unique and engaging scripts - for their chicken rice that is very tasty.

- -
- Light +

{!! str_first_sentence($ai_tool->summary) !!}

+
+ + {{ $ai_tool->pricing_type }} + + + + @if ($ai_tool->is_app_web_both == 'both') + App & Web + @else + {{ ucwords($ai_tool->is_app_web_both) }} + @endif +
+ + +
+ @foreach ($ai_tool->keywords as $keyword) + @if ($loop->iteration <= 3) + {{ $keyword->value }} + @else + + + {{ $ai_tool->keywords->count() - 3 }} more + + @break + @endif + @endforeach
+ + +
diff --git a/resources/views/front/partials/breadcrumbs.blade.php b/resources/views/front/partials/breadcrumbs.blade.php index 81c3f75..6c06be4 100644 --- a/resources/views/front/partials/breadcrumbs.blade.php +++ b/resources/views/front/partials/breadcrumbs.blade.php @@ -5,8 +5,17 @@ @if ($loop->last) @else - + @if (!is_empty($breadcrumb['url'])) + + @else + + @endif @endif @endforeach + +@push('top_head') + {!! $breadcrumb_context !!} +@endpush diff --git a/resources/views/front/partials/search.blade.php b/resources/views/front/partials/search.blade.php new file mode 100644 index 0000000..fefc5fa --- /dev/null +++ b/resources/views/front/partials/search.blade.php @@ -0,0 +1,8 @@ +
+ @csrf + + +
diff --git a/resources/views/front/partials/sidebar.blade.php b/resources/views/front/partials/sidebar.blade.php new file mode 100644 index 0000000..c0586d2 --- /dev/null +++ b/resources/views/front/partials/sidebar.blade.php @@ -0,0 +1,34 @@ + + + + + \ No newline at end of file diff --git a/resources/views/front/search_results.blade.php b/resources/views/front/search_results.blade.php new file mode 100644 index 0000000..a352bcf --- /dev/null +++ b/resources/views/front/search_results.blade.php @@ -0,0 +1,110 @@ +@extends('front.layouts.app') + +@section('content') +
+ @include('front.partials.breadcrumbs') + +
+ @include('front.partials.search') +
+ +

+ {{ $query }} AI tools +

+ +
+ +
+ +
+
+ @foreach ($results as $result) +
+
+
+
+
+ {{ $result->tool_name }} +
+
+
+ +

{{ $result->ai_tool->tool_name }}

+
+

+ @foreach (str_extract_sentences($result->ai_tool->summary, 2) as $sentence) + {!! $sentence !!} + @endforeach +

+ +
+ + {{ $result->ai_tool->pricing_type }} + + + + @if ($result->ai_tool->is_app_web_both == 'both') + App & Web + @else + {{ ucwords($result->ai_tool->is_app_web_both) }} + @endif + +
+ + + +
+ @foreach ($result->ai_tool->keywords as $keyword) + @if ($loop->iteration <= 3) + {{ $keyword->value }} + @else + + + {{ $result->ai_tool->keywords->count() - 3 }} more + + @break + @endif + @endforeach +
+ +
+
+ + + +
+
+ @endforeach + +
+ @if ($results instanceof \Illuminate\Pagination\LengthAwarePaginator) + @if ($results->hasPages()) +
+ {{ $results->onEachSide(1)->links('pagination::bootstrap-5-pagination-limit-10') }} +
+ @endif + @elseif ($results instanceof \Illuminate\Pagination\CursorPaginator) + @if ($results->hasPages()) +
+ {{ $results->links('pagination::simple-bootstrap-5') }} +
+ @endif + @endif +
+
+ Use Search to discover AI tools by brand, task or features. +
+ +
+ +
+ @include('front.partials.sidebar') +
+
+ + + +
+@endsection diff --git a/resources/views/vendor/pagination/bootstrap-4.blade.php b/resources/views/vendor/pagination/bootstrap-4.blade.php new file mode 100644 index 0000000..6b87485 --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-4.blade.php @@ -0,0 +1,51 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/bootstrap-5-pagination-limit-10.blade.php b/resources/views/vendor/pagination/bootstrap-5-pagination-limit-10.blade.php new file mode 100644 index 0000000..1f2b1ba --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-5-pagination-limit-10.blade.php @@ -0,0 +1,73 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/bootstrap-5-pagination.blade.php b/resources/views/vendor/pagination/bootstrap-5-pagination.blade.php new file mode 100644 index 0000000..db602df --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-5-pagination.blade.php @@ -0,0 +1,83 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/bootstrap-5-showing.blade.php b/resources/views/vendor/pagination/bootstrap-5-showing.blade.php new file mode 100644 index 0000000..12e31ef --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-5-showing.blade.php @@ -0,0 +1,11 @@ +@if ($paginator->hasPages()) +

+ {!! __('Showing') !!} + {{ $paginator->firstItem() }} + {!! __('to') !!} + {{ $paginator->lastItem() }} + {{-- {!! __('of') !!} + {{ $paginator->total() }} --}} + {!! __('results') !!} +

+@endif diff --git a/resources/views/vendor/pagination/bootstrap-5.blade.php b/resources/views/vendor/pagination/bootstrap-5.blade.php new file mode 100644 index 0000000..4904c12 --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-5.blade.php @@ -0,0 +1,94 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/default.blade.php b/resources/views/vendor/pagination/default.blade.php new file mode 100644 index 0000000..9399131 --- /dev/null +++ b/resources/views/vendor/pagination/default.blade.php @@ -0,0 +1,47 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/semantic-ui.blade.php b/resources/views/vendor/pagination/semantic-ui.blade.php new file mode 100644 index 0000000..af16ede --- /dev/null +++ b/resources/views/vendor/pagination/semantic-ui.blade.php @@ -0,0 +1,40 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-bootstrap-4.blade.php b/resources/views/vendor/pagination/simple-bootstrap-4.blade.php new file mode 100644 index 0000000..4bb4917 --- /dev/null +++ b/resources/views/vendor/pagination/simple-bootstrap-4.blade.php @@ -0,0 +1,27 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-bootstrap-5.blade.php b/resources/views/vendor/pagination/simple-bootstrap-5.blade.php new file mode 100644 index 0000000..cf2e049 --- /dev/null +++ b/resources/views/vendor/pagination/simple-bootstrap-5.blade.php @@ -0,0 +1,30 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-default.blade.php b/resources/views/vendor/pagination/simple-default.blade.php new file mode 100644 index 0000000..36bdbc1 --- /dev/null +++ b/resources/views/vendor/pagination/simple-default.blade.php @@ -0,0 +1,19 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-tailwind.blade.php b/resources/views/vendor/pagination/simple-tailwind.blade.php new file mode 100644 index 0000000..18b6ef5 --- /dev/null +++ b/resources/views/vendor/pagination/simple-tailwind.blade.php @@ -0,0 +1,29 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/tailwind.blade.php b/resources/views/vendor/pagination/tailwind.blade.php new file mode 100644 index 0000000..647ec1f --- /dev/null +++ b/resources/views/vendor/pagination/tailwind.blade.php @@ -0,0 +1,130 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/routes/tests.php b/routes/tests.php index 5febfdb..224cbdd 100644 --- a/routes/tests.php +++ b/routes/tests.php @@ -21,12 +21,17 @@ $url_to_crawl = UrlToCrawl::find(1); - if (is_null($url_to_crawl)) - { - return ; + if (is_null($url_to_crawl)) { + return; } GetUrlBodyJob::dispatch($url_to_crawl->id)->onQueue('default')->onConnection('default'); return 'ok'; -}); \ No newline at end of file +}); + +Route::get('/epoch', function () { + + dd(epoch_now_timestamp(1)); + +}); diff --git a/routes/web.php b/routes/web.php index d25f593..a6cd0c0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -51,6 +51,20 @@ }); +Route::prefix('ai-search')->group(function () { + + Route::post('/', [\App\Http\Controllers\Front\FrontSearchController::class, 'search'])->name('front.search.post'); + + Route::get('/{query}', [\App\Http\Controllers\Front\FrontSearchController::class, 'searchResult'])->name('front.search.results'); + +}); + +Route::prefix('ai-tool')->group(function () { + + Route::get('/{ai_tool_slug}', [\App\Http\Controllers\Front\FrontToolController::class, 'show'])->name('front.aitool.show'); + +}); + Route::get('/terms', [App\Http\Controllers\Front\FrontHomeController::class, 'terms'])->name('front.terms')->middleware('cacheResponse:2630000'); Route::get('/privacy', [App\Http\Controllers\Front\FrontHomeController::class, 'privacy'])->name('front.privacy')->middleware('cacheResponse:2630000');