Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
TURNSTILE_PUBLIC=1x00000000000000000000AA
DEV_API_TARGET=http://localhost:8000
20 changes: 15 additions & 5 deletions apps/frontend/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,16 @@ export default defineNuxtConfig({
htmlAttrs: {
lang: 'en',
},
script: [
{
src: 'https://tally.so/widgets/embed.js',
},
],
},
},
sitemap: {
sitemapsPathPrefix: '/',
zeroRuntime: true,
},
ogImage: {
fonts: [
Expand Down Expand Up @@ -99,14 +105,18 @@ export default defineNuxtConfig({
name: 'Blueprint',
},
nitro: {
// Allow switching dev API target via DEV_API_TARGET (e.g., https://blueprint.zip)
// Falls back to local backend when unset.
devProxy: {
'/api': {
// Change to https://blueprint.zip/api to use the production API
// Local API is http://localhost:8000/api
target: 'http://localhost:8000/api',
// Local API was previously at http://localhost:8000/api — keep behaviour by stripping trailing slash from DEV_API_TARGET when set.
target: process.env.DEV_API_TARGET?.replace(/\/$/, '') || 'http://localhost:8000',
changeOrigin: true,
Comment thread
darkzmaj marked this conversation as resolved.
},
'/browse/sitemap.xml': {
target: process.env.DEV_API_TARGET?.replace(/\/$/, '') || 'http://localhost:8000',
changeOrigin: true,
},
'/browse/sitemap.xml': 'http://localhost:8000/browse/sitemap.xml',
'/yay': 'https://blueprint.zip/yay',
},
routeRules: {
Expand All @@ -125,7 +135,7 @@ export default defineNuxtConfig({
},
},
plausible: {
apiHost: 'https://blueprint.zip/yay',
apiHost: 'https://plausible.prpl.wtf',
domain: 'blueprint.zip',
autoOutboundTracking: true,
ignoredHostnames: ['localhost'],
Expand Down
81 changes: 81 additions & 0 deletions apps/frontend/src/components/ui/browse/Filters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@
</button>
</div>
</div>

<div
class="divide-y divide-neutral-700 rounded-2xl border border-neutral-700 transition-colors focus-within:divide-neutral-500 focus-within:border-neutral-500"
>
<div class="flex items-center gap-1.5 p-2 font-bold transition-colors">
<Icon
name="memory:cash"
:size="22"
mode="svg"
class="block"
/>
<span>Price</span>
</div>
<div class="space-y-3 p-4 transition-colors">
<input
type="range"
min="0"
max="1000"
step="10"
v-model.number="props.form.maxPrice"
class="slider block w-full rounded-lg appearance-none cursor-pointer"
/>
<div class="flex justify-between text-xs text-default-font/40">
<span>Free</span>
<span>Paid</span>
</div>
</div>
</div>

<div class="group">
<button
class="focus:text-brand-50 hover:text-brand-50 w-full cursor-pointer rounded-t-2xl border border-neutral-700 p-2 outline-0 transition-colors group-focus-within:border-neutral-500"
Expand Down Expand Up @@ -81,6 +110,7 @@ const props = defineProps<{
sortBy: string
showExtensions: boolean
showThemes: boolean
maxPrice: number
}
}>()

Expand All @@ -90,3 +120,54 @@ const sortOptions = [
{ value: 'created', label: 'Newest First' },
]
</script>

<style scoped>
.slider {
background: white;
height: 6px;
border-radius: 999px;
}

.slider::-webkit-slider-runnable-track {
background: white;
height: 6px;
border-radius: 999px;
}

.slider::-moz-range-track {
background: white;
height: 6px;
border-radius: 999px;
}

.slider::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #6db7ff;
cursor: pointer;
border: 2px solid #6db7ff;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
margin-top: -6px;
}

.slider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #6db7ff;
cursor: pointer;
border: 2px solid #6db7ff;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
margin-top: -6px;
}

.slider::-webkit-slider-thumb:hover {
transform: scale(1.1);
}

.slider::-moz-range-thumb:hover {
transform: scale(1.1);
}
</style>
23 changes: 23 additions & 0 deletions apps/frontend/src/pages/browse/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,22 @@ const form = ref({
sortBy: 'popularity',
showExtensions: true,
showThemes: true,
Comment thread
darkzmaj marked this conversation as resolved.
// Note: Despite the name, this is used as a pricing filter flag:
// 0 = show free only, non-zero = show paid only.
maxPrice: 0,
})

const getMinPrice = (extension: Extension): number => {
const prices = Object.values(extension.platforms)
.map(p => p.price)
.filter(p => p >= 0)
return prices.length > 0 ? Math.min(...prices) : 0
}

const isFree = (extension: Extension): boolean => {
return getMinPrice(extension) === 0
}
Comment thread
darkzmaj marked this conversation as resolved.

const filteredAndSortedExtensions = computed(() => {
if (!extensions.value) return []

Expand All @@ -178,6 +192,15 @@ const filteredAndSortedExtensions = computed(() => {
return true
})

filtered = filtered.filter((extension) => {
const minPrice = getMinPrice(extension)
if (form.value.maxPrice === 0) {
return minPrice === 0
}
// any non-zero maxPrice shows all price tiers (free and paid)
return true
})

switch (form.value.sortBy) {
case 'popularity':
filtered.sort((a, b) => b.stats.panels - a.stats.panels)
Expand Down