diff --git a/src/components/RecommendationWizard.vue b/src/components/RecommendationWizard.vue new file mode 100644 index 0000000..895c30f --- /dev/null +++ b/src/components/RecommendationWizard.vue @@ -0,0 +1,145 @@ + + + + + + + + Step {{ step + 1 }} of {{ totalSteps }} + + + + + What kind of tool are you looking for? + + Select a category + Social + Messaging + Tools + Protocols + All + + + + + + Is beginner-friendliness important? + + + + + Yes + + + + + Nope + + + + + + + How comfortable are you with self-hosting? + + + {{ + ['No experience', 'Beginner', 'Somewhat confident', 'Experienced', 'Pro'][ + answers.selfHostingLevel - 1 + ] + }} + + + + + + + + Back + + + + + + + + Next + + + + + Show Recommendations + + + + + + Restart Wizard + + + diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 2e14707..af2d970 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -5,6 +5,7 @@ import AppSearch from '@/components/form/AppSearch.vue'; import CategorySelector from '@/components/form/CategorySelector.vue'; import ReshuffleButton from '@/components/form/ReshuffleButton.vue'; import SurpriseMeButton from '@/components/form/SurpriseMeButton.vue'; +import RecommendationWizard from '@/components/RecommendationWizard.vue'; import { useSEO } from '@/composables/useSEO'; import { apps } from '@/data/apps'; import { categories } from '@/data/categories'; @@ -35,15 +36,24 @@ const suggestions = apps.flatMap((app) => app.alternatives || []); const orderedApps = computed(() => { // Access shuffleTrigger to ensure re-computation on each shuffle globalStore.shuffleTrigger; - return globalStore.isReshuffled - ? shuffleAppsPurely(apps) + return globalStore.isReshuffled + ? shuffleAppsPurely(apps) : sortAppsByLinksThenRandom(apps); }); +const selectedHostingLevel = ref<1 | 2 | 3 | null>(null); + const filteredApps = computed(() => { - return filterApps(orderedApps.value, selectedCategory.value, searchQuery.value); + let result = filterApps(orderedApps.value, selectedCategory.value, searchQuery.value); + + if (selectedHostingLevel.value !== null) { + result = result.filter(app => app.selfHostingLevel === selectedHostingLevel.value); + } + + return result; }); + const title = computed(() => t('app.title')); const subtitleBase = computed(() => { const isMobile = window.innerWidth <= 600; @@ -101,6 +111,22 @@ function handleAbrirModal(app: App) { }); } +function handleWizardResults(answers: { + category: CategoryId | null; + beginnerFriendly: boolean | null; + selfHostingLevel: 1 | 2 | 3 | null; +}) { + if (answers.category) selectedCategory.value = answers.category; + if (answers.beginnerFriendly !== null) { + searchQuery.value = answers.beginnerFriendly ? 'beginner' : ''; + } + if (answers.selfHostingLevel !== null) { + selectedHostingLevel.value = answers.selfHostingLevel; + } + + showFilters.value = true; +} + function handleSurpriseMe(app: App) { // Reuse existing modal logic handleAbrirModal(app); @@ -199,6 +225,10 @@ watch([searchQuery, selectedCategory], ([query, category]) => { + + + + { /> - @@ -220,10 +250,35 @@ watch([searchQuery, selectedCategory], ([query, category]) => { - + class="grid grid-cols-1 w-full max-w-full md:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-12 mt-2" +> + + + + + + + 😕 No apps found for this category. + Try a different option or + { selectedCategory = 'all'; searchQuery = ''; selectedHostingLevel = null; }" + class="underline text-color hover:text-brand transition" + > + restart the wizard + . + + + + + - -
+ 😕 No apps found for this category. + Try a different option or + { selectedCategory = 'all'; searchQuery = ''; selectedHostingLevel = null; }" + class="underline text-color hover:text-brand transition" + > + restart the wizard + . +