use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
+use Typesense\Client;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+ private $client;
+
public function __construct()
{
// if(config('app.env') === 'production') {
);
}
+ public function connect() {
+ $this->client = new Client(
+ [
+ 'api_key' => env('TYPESENSE_API_KEY', 'K4fae5KYZTVj6Wucp5q9'),
+ 'nodes' => [
+ [
+ 'host' => env('TYPESENSE_HOST', 'typesense.dev.prescription-quotidien.com'), // For Typesense Cloud use xxx.a1.typesense.net
+ 'port' => '', // For Typesense Cloud use 443
+ 'protocol' => env('TYPESENSE_PROTOCOL', 'https'), // For Typesense Cloud use https
+ ],
+ ],
+ 'connection_timeout_seconds' => 2,
+ ]
+ );
+ }
+
public function getPdfBySlug() {
- $refs = request('refs', []);
+ if(null === $this->client) {
+ $this->connect();
+ }
+ $q = request('query', '');
+ $page = request('page', 1);
+ $searchParameters = [
+ 'q' => $q,
+ 'query_by' => 'text',
+ 'per_page' => 20,
+ 'page' => $page
+ ];
+
+ $typeSenseResults = $this->client->collections['fluidbooks']->documents->search($searchParameters);
+ $refs = array_map(function($hits) { return $hits['document']['reference']; }, $typeSenseResults['hits']);
+
$pdfFile = new PdfFile();
return $pdfFile->whereIn('slug', $refs)->get()->toArray();
}
protected static function data()
{
return [
- 'algolia_app_id' => config('scout.algolia.id'),
- 'algolia_search_key' => config('scout.algolia.search_key'),
- 'algolia_prefix' => config('scout.prefix'),
+ 'typesense_api_key' => config('scout.typesense.api_key'),
+ 'typesense_host' => config('scout.typesense.host'),
+ 'typesense_prefix' => config('scout.prefix'),
];
}
public static function output()
{
- $json = json_encode(self::data());
+ $json = ""; //json_encode(self::data());
return 'const php_vars = '.$json;
*
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
*/
-
+console.log(process.env.MIX_TYPESENSE_HOST)
const files = require.context('./', true, /\.vue$/i)
files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default))
-<template>
-
- <div class="card w-100">
- <img class="card-img-top" :src="hit.coverUrl" alt="Cover">
- <div class="card-body">
- <h5 class="card-title text-center" v-text="hit.title"></h5>
- </div>
-
- <a :href="viewLink" class="btn btn-big mb-0" target="_blank"><i class="fas fa-book-open mr-1"></i> Lire cette édition</a>
- </div>
-
-</template>
-
-<script>
- export default {
- name: "FileHit",
- props: ['hit'],
- data(){
- return {
-
- }
- },
- computed: {
- viewLink: function () {
- let page = this.hit._highlightResult.content.matchLevel === 'none' ?
- '1' :
- this.hit.page;
-
- return `/view/${this.hit.file.slug}#page=${page}`;
- }
-
- }
- }
-</script>
-
-<style scoped>
-
-</style>
<template>
<div>
<template>
- <ais-instant-search :search-client="searchClient" index-name="fluidbooks">
- <ais-search-box/>
- <ais-hits v-slot="{items}">
- <div class="item-pdf-archive" v-for="item in items" :key="item.id">
+ <div class="py-4 xl:py-16 mb-4 xl:mb-24 bg-clearblue">
+ <div class="container">
+ <h1 class="bigtitle !mb-4 xl:!mb-6 text-center xl:text-left">Nos archives</h1>
+ <form action="" class="searchform flex items-center gap-[15px] px-6 py-3 bg-white border-1 border-[#DCE0F5] rounded-[3px]">
+ <div>
+ <img :src="SearchIcon" alt="Lancer la recherche" />
+ </div>
+ <input class="color-blue w-100 outline-0 font-medium" type="text" placeholder="Recherche..." v-model="query">
+ </form>
+ </div>
+ </div>
+ <div class="container mb-16">
+ <div class="flex flex-wrap gap-x-[16px] md:gap-x-[32px] gap-y-7 md:gap-y-16" id="ais-Hits-container">
+ <div class="item-pdf-archive" v-for="item in results" :key="item.id">
<div class="cover">
- <a class="img-link cursor-pointer" href="" target="_blank">
- <img class="d-block cover-over box-shadow-cover" :src="item.document.coverUrl" alt="">
+ <a class="img-link cursor-pointer" target="_blank" href="">
+ <img class="d-block cover-over box-shadow-cover" :src="item.coverUrl" alt="">
<div class="shadowcover">
<img class="max-h-[39px]" src="" />
</div>
</a>
<div class="cover-title text-left bg-transparent max-xs:!-mt-1">
- <p class="font-bold bg-transparent">{{item.document.title}}</p>
+ <p class="font-bold bg-transparent">{{item.title}}</p>
<a href="" class="underline font-medium">Lire cette édition</a>
</div>
</div>
</div>
- </ais-hits>
- </ais-instant-search>
+ </div>
+ </div>
+ <div
+ v-if="!isLastPage"
+ id="scroll-sentinel"
+ style="height: 1px;"
+ ></div>
</template>
</div>
</template>
<script>
-import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";
-import axios from "axios";
-
-const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
- server: {
- apiKey: process.env.MIX_TYPESENSE_API_KEY,
- nodes: [
- {
- host: process.env.MIX_TYPESENSE_HOST,
- port: "",
- protocol: "https",
- },
- ],
- cacheSearchResultsForSeconds: 2 * 60,
- },
- additionalSearchParameters: {
- query_by: "text",
- },
-});
-const searchClient = typesenseInstantsearchAdapter.searchClient;
-
-const originalSearch = searchClient.search.bind(searchClient);
-searchClient.search = async function(queries) {
- const startTime = performance.now();
-
- const tsResults = await originalSearch(queries);
- const refs = tsResults.results[0].hits.map(h => h.reference)
- const res = await axios.post("/retrieveLetter", { refs });
-
- tsResults.results[0].hits = res.data.map(doc => ({ document: doc }));
-
- const endTime = performance.now();
-
- const elapsedTime = endTime - startTime;
-
- console.log(`Le temps écoulé est de ${elapsedTime} ms`);
-
- return tsResults;
-};
+import SearchIcon from '../../../../public/img/search_icon.svg'
export default {
+ props: ['isConnected'],
data(){
- return{
- searchClient
- }
+ return{
+ SearchIcon: SearchIcon,
+ isLastPage: false,
+ observer: null,
+ container: null,
+ query: null,
+ lastResults: [],
+ page: 1,
+ results: [],
+ isLoading: false
+ }
},
- mounted(){
+ mounted() {
+ this.container = document.getElementById('scroll-sentinel')
+ const $this = this
+
+ // Premier affichage
+ $this.searchArchive()
+
+ document.addEventListener('scroll', async function() {
+ const bottom = window.innerHeight + window.scrollY
+ if(!$this.isLoading && Math.round(bottom) >= Math.round($this.container.offsetTop) - 100) {
+ $this.isLoading = true
+ $this.page += 1
+ try {
+ await $this.searchArchive()
+ }finally {
+ $this.isLoading = false
+ }
+ }
+ })
},
- computed: {
+ watch: {
+ query(after, before) {
+ this.page = 1;
+ this.searchArchive();
+ }
},
+ methods: {
+ searchArchive() {
+ const $this = this
+ return axios.get('/retrieveLetter', { params: { query: this.query, page: $this.page } })
+ .then(function(response) {
+ if($this.page === 1) {
+ $this.results = response.data;
+ }else {
+ $this.results = [...$this.results,...response.data];
+ }
+ })
+ .catch(error => {
+ console.log(error)
+ });
+ }
+ }
};
</script>
<template>
- <input v-model="text" class="color-blue w-100 outline-0 font-medium" type="text" placeholder="Recherche..."/>
+
</template>
<script>
-import Typesense from 'typesense'
export default {
name: "Search",
data() {
return {
- text: '',
- client: null
- }
- },
- watch: {
- text(newText) {
- console.log(newText)
- let searchParameters = {
- 'q' : newText,
- 'query_by' : 'text',
- 'per_page': 20,
- 'page': 1
- }
- this.client.collections('fluidbooks')
- .documents()
- .search(searchParameters)
- .then(function (searchResults) {
- console.log(searchResults)
- })
}
},
- mounted() {
- this.client = new Typesense.Client({
- 'nodes': [{
- 'host': process.env.MIX_TYPESENSE_HOST, // For Typesense Cloud use xxx.a1.typesense.net
- 'port': '', // For Typesense Cloud use 443
- 'protocol': 'https' // For Typesense Cloud use https
- }],
- 'apiKey': process.env.MIX_TYPESENSE_API_KEY,
- 'connectionTimeoutSeconds': 3600
- })
- }
}
</script>
+.ais-SearchBox-form {
+ display: flex;
+ gap: 16px;
+ background-color: white;
+ border-radius: 3px;
+ border: 1px solid #DCE0F5;
+ padding: 17px 24px;
+}
.ais-SearchBox-input {
- @extend .form-control;
- width: calc(100% - 80px);
+ width: 100%;
display: inline-block;
+ outline: none;
+ font-weight: 500;
+ color: $blue;
+ line-height: 1;
+ &::placeholder {
+ color: $blue;
+ }
+}
+.ais-SearchBox-submit {
+ order: -1;
+ svg {
+ width: 13px;
+ height: 13px;
+ fill: #044e9c;
+ }
}
.ais-SearchBox-reset {
display: none;
padding: 0;
background-color: #fff252;
}
+.ais-Pagination {
+ position: absolute;
+ visibility: hidden;
+}
+
Route::get('devenez-annonceurs', 'AdvertisersController@index')->name('annonceurs.index');
Route::post('devenez-annonceurs', 'AdvertisersController@requestMail')->name('annonceurs.store');
- Route::post('retrieveLetter', 'Controller@getPdfBySlug');
+ Route::match(['post','get'],'retrieveLetter', 'Controller@getPdfBySlug');
});
/** Public routes + NGROK routes */