-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathindex.html
More file actions
247 lines (237 loc) · 16.7 KB
/
Copy pathindex.html
File metadata and controls
247 lines (237 loc) · 16.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<!DOCTYPE html>
<html>
<head>
<title>Checkjebon.nl - Goedkopere boodschappen</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<meta name="description" content="Gebruik je boodschappenlijstje om er achter te komen waar je het goedkoopst kunt winkelen." />
<link rel="stylesheet" href="/css/site/style.css">
<link rel="stylesheet" href="/css/googlefonts/lato.css">
<link rel="stylesheet" href="/css/fontawesome/font-awesome.min.css">
<!-- Vue -->
<script src="/js/vue/vue.min.js"></script>
<!-- Tailwind CSS -->
<script src="/js/tailwindcss/tailwindcss.js"></script>
<!-- Text recognition -->
<script src="/js/tesseract/tesseract.min.js"></script>
<script src="/js/fuse/fuse.js"></script>
<!-- Checkjebon-js -->
<script src="https://supermarkt.github.io/checkjebon-js/checkjebon.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { sans: ['Lato', 'ui-sans-serif', 'system-ui'] },
},
},
}
</script>
</head>
<body class="bg-gray-50 min-h-screen font-sans flex flex-col">
<main class="flex-1 flex flex-col items-center justify-center w-full">
<div class="w-full max-w-2xl py-12 px-4" id="checkjebon">
<div class="w-full max-w-2xl">
<div class="bg-white rounded-2xl shadow-xl p-8">
<h1 class="text-3xl font-extrabold text-center text-gray-900 tracking-tight mb-2">Checkjebon.nl</h1>
<p class="text-center text-gray-500 mb-6">Bekijk wat je kwijt zou zijn voor dezelfde boodschappen bij een andere supermarkt en bespaar elke keer dat je naar de winkel gaat.</p>
<div class="mb-4">
<textarea id="shoppinglist" class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none text-base" v-model="shoppinglist" @input="update" :disabled="isSearching" placeholder="Tik hier je boodschappenlijstje. halfvolle melk volkoren brood zilvervliesrijst bananen stokbrood falafel Op zoek naar een specifiek merk? Tik de merknaam er dan bij." title="Vul hier je boodschappenlijst in"></textarea>
</div>
<div class="flex items-center justify-center mb-4">
<input id="shopMultiple" type="checkbox" v-model="shopMultipleSupermarkets" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500">
<label for="shopMultiple" class="ml-2 text-sm font-medium text-gray-700">Winkel bij meerdere supermarkten</label>
</div>
<div class="flex flex-col sm:flex-row flex-wrap justify-center gap-2 sm:gap-3 mt-2">
<button type="button" class="inline-flex items-center justify-center sm:justify-start px-5 py-2 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-semibold rounded-lg shadow transition disabled:opacity-50 flex-shrink-0" @click="search" :disabled="isSearching || !shoppinglist || shoppinglist.trim() === ''" title="Vergelijk prijzen" aria-label="Vergelijk prijzen">
<i class="fa fa-search mr-2" aria-hidden="true"></i>
<span class="sr-only">{{ isSearching ? 'Bezig met zoeken...' : (shopMultipleSupermarkets ? 'Kies supermarkten' : 'Vergelijk prijzen') }}</span>
<span aria-hidden="true">{{ isSearching ? 'Bezig met zoeken...' : (shopMultipleSupermarkets ? 'Kies supermarkten' : 'Vergelijk prijzen') }}</span>
</button>
<div class="flex flex-row flex-wrap justify-center gap-2 sm:gap-3">
<button type="button" class="inline-flex items-center justify-center sm:justify-start px-5 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold rounded-lg shadow transition disabled:opacity-50 flex-shrink-0" @click="example" :disabled="isSearching" title="Voorbeeld" aria-label="Voorbeeld">
<i class="fa fa-book sm:mr-2" aria-hidden="true"></i>
<span class="sr-only">Voorbeeld</span>
<span aria-hidden="true" class="hidden sm:inline">Voorbeeld</span>
</button>
<button type="button" class="inline-flex items-center justify-center sm:justify-start px-5 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold rounded-lg shadow transition disabled:opacity-50 flex-shrink-0" @click="share" :disabled="isSearching" title="Delen" aria-label="Delen">
<i class="fa fa-share-alt sm:mr-2" aria-hidden="true"></i>
<span class="sr-only">Delen</span>
<span aria-hidden="true" class="hidden sm:inline">Delen</span>
</button>
<button type="button" class="inline-flex items-center justify-center sm:justify-start px-5 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 font-semibold rounded-lg shadow transition disabled:opacity-50 flex-shrink-0" @click="clear" :disabled="isSearching" title="Wissen" aria-label="Wissen">
<i class="fa fa-trash" aria-hidden="true"></i>
<span class="sr-only">Wissen</span>
</button>
</div>
</div>
</div>
</div>
<div class="w-full flex justify-center mt-10" id="supermarket-selection" v-if="showSupermarketSelection">
<div class="bg-white rounded-2xl shadow-xl p-8 max-w-2xl w-full">
<h2 class="text-2xl font-bold text-center text-gray-900 mb-6">Winkelplan opstellen</h2>
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 mb-2">Tot hoeveel supermarkten wil je bezoeken?</label>
<select v-model="maxSupermarkets" @change="updateShoppingPlan" class="w-full p-2 border border-gray-300 rounded-lg">
<option :value="null" disabled>Kies een aantal...</option>
<option v-for="n in availableSupermarkets.length" :value="n">{{ n }}</option>
</select>
</div>
<div v-if="maxSupermarkets">
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 mb-2">Kies je supermarkten:</label>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-6 justify-center justify-items-center">
<div v-for="store in availableSupermarkets" :key="store.code" class="flex">
<input type="checkbox" :id="'store-' + store.code" :value="store.code" v-model="selectedSupermarketCodes" @change="updateShoppingPlan" class="hidden">
<label :for="'store-' + store.code" class="cursor-pointer flex flex-col items-center p-3 w-34 rounded-lg transition" :class="selectedSupermarketCodes.includes(store.code) ? 'bg-yellow-50 border border-yellow-300 ring-2 ring-yellow-300' : 'bg-white border border-gray-100 hover:shadow-md'">
<img :src="store.icon" class="w-24 h-24 rounded-full mb-2 object-cover" v-if="store.icon">
<span class="text-sm text-gray-900 text-center">{{ store.name }}</span>
</label>
</div>
</div>
</div>
<div class="flex flex-col sm:flex-row flex-wrap justify-center gap-2 sm:gap-3 mt-2">
<button v-if="selectedSupermarketCodes.length > 0" class="inline-flex items-center justify-center sm:justify-start px-5 py-2 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-semibold rounded-lg shadow transition disabled:opacity-50 flex-shrink-0" @click="createShoppingPlan(true)" :disabled="isSearching">
<i class="fa fa-search mr-2" aria-hidden="true"></i>
{{ isSearching ? 'Winkelplan samenstellen...' : 'Maak winkelplan' }}
</button>
</div>
</div>
</div>
</div>
<div class="w-full flex justify-center mt-10" id="supermarkets" v-show="supermarkets.length > 0">
<div class="bg-white rounded-2xl shadow-xl p-8 max-w-2xl w-full">
<h2 class="text-2xl font-bold text-center text-gray-900 tracking-tight mb-6 uppercase">Supermarkten</h2>
<ul class="divide-y divide-gray-200">
<li class="flex items-center py-4 cursor-pointer transition hover:bg-yellow-50 px-2" :class="{'bg-yellow-100': supermarket.code == selectedSupermarket?.code}" v-for="supermarket in supermarkets" :key="supermarket.code" @click="selectedSupermarket=supermarket; select()">
<img v-bind:src="supermarket.icon" class="rounded-full mr-4 shadow" style="width: 60px" :title="'Logo ' + supermarket.name">
<div class="flex-grow">
<a href="#" class="font-semibold text-lg text-gray-900 hover:underline" onclick="return false;" :title="supermarket.name + ': ' + $options.filters.formatPrice(supermarket.totalPrice) + ' euro'">
{{supermarket.name}}
</a>
<div class="text-sm text-gray-500 mt-1">
<span v-if="supermarket.notFound > 0">
<i class="fa fa-info-circle"></i> {{supermarket.notFound}} van {{supermarket.products.length | formatCount}} niet gevonden in {{supermarket.totalProducts | formatCount}} producten
</span>
<span v-if="supermarket.notFound == 0">
{{supermarket.totalProducts | formatCount}} producten
</span>
</div>
</div>
<div class="text-xl font-bold text-right text-gray-800 ml-4" style="width: 80px;">
{{ supermarket.totalPrice | formatPrice }}
</div>
</li>
</ul>
<p class="text-center text-gray-400 text-sm mt-6">
Waarom sommige <a href="#" onclick="alert('Een aantal grote supermarktketens zoals Nettorama en Boni maar ook online bezorgservices zoals GORILLAS en Crisp hebben geen prijzen op hun website staan. Hierdoor kunnen ze ook niet meegenomen worden in de prijsvergelijking van Checkjebon.nl.'); return false;" target="_blank" class="text-blue-600 hover:underline">supermarkten ontbreken</a>, zoals Nettorama, Boni, etc.
</p>
</div>
</div>
<div class="w-full flex justify-center mt-10" id="articles" v-if="selectedSupermarket && !shoppingPlan">
<supermarket-products :supermarket="selectedSupermarket" :show-share-button="true" @check="check" @edit="edit" @share="share"></supermarket-products>
</div>
<div class="w-full flex flex-col items-center mt-10" id="shopping-plan" v-if="shoppingPlan">
<div class="w-full max-w-2xl mb-4 text-center">
<div v-if="shoppingPlan.supermarkets.length > 1" class="bg-white rounded-lg p-4 mb-4">
<h2 class="text-xl font-semibold mb-2">Winkelplan overzicht</h2>
<div class="overflow-x-auto">
<table class="w-full text-left">
<tbody>
<tr v-for="s in shoppingPlan.supermarkets" :key="s.code" class="border-t">
<td class="py-2">{{ s.name }}</td>
<td class="py-2 text-right">{{ s.totalPrice | formatPrice }}</td>
</tr>
<tr class="border-t font-bold">
<td class="py-2">Totaal</td>
<td class="py-2 text-right">{{ shoppingPlan.totalCost | formatPrice }}</td>
</tr>
<tr v-if="shoppingPlan.bestSingleTotal != null" class="border-t">
<td class="py-2">Als je alle producten bij de goedkoopste winkel koopt ({{ shoppingPlan.bestSingleName }})</td>
<td class="py-2 text-right">{{ shoppingPlan.bestSingleTotal | formatPrice }}</td>
</tr>
<tr v-if="shoppingPlan.bestSingleTotal != null" class="border-t bg-green-50">
<td class="py-2">Extra besparing</td>
<td class="py-2 text-right text-green-700 font-semibold">{{ shoppingPlan.totalSavings | formatPrice }}</td>
</tr>
</tbody>
</table>
</div>
<div class="flex justify-center mt-6">
<share-button @share="$emit('share')"></share-button>
</div>
</div>
<div v-else class="bg-white rounded-lg p-4 mb-4">
<h2 class="text-2xl font-bold text-gray-900">Totaalprijs: {{ shoppingPlan.totalCost | formatPrice }}</h2>
<p class="text-gray-500">Aantal winkels: {{ shoppingPlan.supermarkets.length }}</p>
</div>
</div>
<div v-for="supermarket in shoppingPlan.supermarkets" :key="supermarket.code" class="w-full flex justify-center mb-8">
<supermarket-products :supermarket="supermarket" :show-share-button="false" @check="check" @edit="edit" @share="share"></supermarket-products>
</div>
</div>
</div>
</main>
<footer class="w-full bg-gray-100 border-t mt-auto">
<div class="max-w-2xl mx-auto py-8 px-4">
<h2 class="text-lg font-bold text-center text-gray-900 tracking-tight mb-2 uppercase">Over Checkjebon.nl</h2>
<p class="text-center text-gray-500 text-sm">Prijzen in supermarkten veranderen regelmatig, Checkjebon.nl werkt ze <abbr :title="pricesLastUpdated ? 'Laatst bijgewerkt op ' + pricesLastUpdated : null">dagelijks</abbr> bij en vertelt je waar je het goedkoopst uit bent voor je boodschappen. Verder is Checkjebon.nl een <a href="https://www.github.com/supermarkt/checkjebon/" target="_blank" class="text-blue-600 hover:underline">open source project</a> en ook de gebruikte <a href="https://github.com/supermarkt/checkjebon/blob/main/data/supermarkets.json" target="_blank" class="text-blue-600 hover:underline">supermarktdata</a> is gratis te hergebruiken in andere projecten. Neem <a href="mailto:info@checkjebon.nl" class="text-blue-600 hover:underline">contact op</a> voor vragen of suggesties.</p>
</div>
</footer>
<script type="text/x-template" id="share-button-template">
<button type="button" class="inline-flex items-center px-5 py-2 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-semibold rounded-lg shadow transition disabled:opacity-50" @click="$emit('share')" title="Deel je boodschappenlijst" aria-label="Deel je boodschappenlijst">
<i class="fa fa-share-alt mr-2" aria-hidden="true"></i>
<span>Deel je boodschappenlijst</span>
<span aria-hidden="true" class="hidden sm:inline">Deel je boodschappenlijst</span>
</button>
</script>
<script type="text/x-template" id="supermarket-products-template">
<div class="bg-white rounded-2xl shadow-xl p-8 max-w-2xl w-full">
<h2 class="text-2xl font-bold text-center text-gray-900 tracking-tight mb-6 uppercase">{{ supermarket?.name }}</h2>
<ul class="divide-y divide-gray-200">
<li class="flex items-center py-4 transition px-2 hover:bg-yellow-50 cursor-pointer" :class="{'bg-yellow-100': product.checked}" v-for="product in supermarket.products">
<input type="checkbox" class="form-checkbox h-5 w-5 text-blue-600 mr-4" :checked="product.checked" @click="$emit('check', product, $event)">
<div class="flex-grow">
<span :class="['font-semibold text-base', product.checked ? 'line-through text-gray-400' : 'text-gray-900']">
<a v-if="!product.isEstimate" v-bind:href="product.link" target="_blank" :class="product.checked ? 'line-through text-gray-400 hover:underline' : 'hover:underline'" :title="product.originalQuery?.replace(/^x\s/, '')">
{{ product.name }}
</a>
<span v-if="!product.price || product.isEstimate">
{{product.originalQuery?.replace(/^x\s/, '')}}
</span>
</span>
<div class="text-sm text-gray-500 mt-1">
<span v-if="product.amount">
<i class="fa fa-pencil cursor-pointer" @click="$emit('edit', product, $event, 'Bedoelde je soms iets anders? Pas dan de naam van dit product aan.\n\nTip: Hoe specifieker je bent, hoe beter het resultaat. Gebruik bijvoorbeeld "smeerboter" in plaats van "boter" of "1,5 liter halfvolle melk" in plaats van alleen "melk".')" title="Product aanpassen"></i>
{{ product.amount | formatAmount }}
</span>
<span v-if="!product.price && !product.isEstimate">
<i class="fa fa-exclamation-triangle cursor-pointer" @click="$emit('edit', product, $event, 'Dit product kon bij geen enkele supermarkt gevonden worden.\n\nPas de naam of hoeveelheid aan op je boodschappenlijst en probeer het opnieuw.')" title="Product aanpassen"></i>
Niet gevonden
</span>
<span v-if="product.price && product.isEstimate">
Niet gevonden, geschatte prijs
<i class="fa fa-question-circle cursor-pointer" @click="$emit('edit', product, $event, 'Het lijkt er op dat deze supermarkt dit product niet onder deze naam op hun website heeft staan. Om toch goed je boodschappenlijstje te kunnen vergelijken, is uitgegaan van de gemiddelde prijs van dit product bij andere winkels.\n\nOm het product toch te kunnen vinden, pas de naam of hoeveelheid aan om iets minder specifiek te zoeken.')" title="Product aanpassen"></i>
</span>
</div>
</div>
<div class="text-lg font-bold text-right text-gray-800 ml-4" style="width: 80px;">
<a v-bind:href="product.link" target="_blank" class="hover:underline" :title="product.name + ': ' + $options.filters.formatPrice(product.price)">
{{ !product.link && product.price ? "~" : "" }}{{ product.price | formatPrice }}
</a>
</div>
</li>
</ul>
<div class="flex justify-center mt-4" v-if="supermarket.totalPrice != null">
<div class="text-xl font-bold text-gray-800 text-center">
Totaal {{ supermarket?.name }}: {{ supermarket.totalPrice | formatPrice }}
</div>
</div>
<div class="flex justify-center mt-6" v-if="showShareButton">
<share-button @share="$emit('share')"></share-button>
</div>
</div>
</script>
<script src="/js/site/script.js"></script>
</body>
</html>