-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: update stop tasks for option-based submission (#52)
* first commit for nyhetskvartalet * updated to use database instead of mock. Added tests * fixed mistake * add canvas-confetti * add celebration component * make stopsuccesspage * fixed another mistake * fixed tests and api error * tiny fix in test * display next stop * fix: add missing failure translations and next stop button * wire stop submission for failed/completed task flows * use task options for news stop * use headline in metadata json * lint --------- Co-authored-by: kristoffer <kristoffersolsen@gmail.com>
- Loading branch information
Showing
18 changed files
with
738 additions
and
211 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| <template> | ||
| <div class="flex flex-col items-center justify-center min-h-screen text-center p-6 bg-white"> | ||
| <div class="mb-8 p-4 bg-red-100 rounded-full"> | ||
| <span class="text-6xl">❌</span> | ||
| </div> | ||
|
|
||
| <h1 class="text-4xl font-bold text-gray-900 mb-2"> | ||
| {{ t('failure.stop_failed_title') }} | ||
| </h1> | ||
|
|
||
| <p class="text-2xl text-gray-600 mb-8"> | ||
| {{ completedStopName }} | ||
| </p> | ||
|
|
||
| <RouterLink | ||
| :to="{ name: 'pupil-map' }" | ||
| class="px-8 py-3 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition-colors" | ||
| > | ||
| {{ t('success.return_to_map') }} | ||
| </RouterLink> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { computed, onMounted } from 'vue'; | ||
| import { useRoute } from 'vue-router'; | ||
| import { useI18n } from 'vue-i18n'; | ||
| import { useMapStore } from '@/map/model/map.store'; | ||
| const route = useRoute(); | ||
| const { t } = useI18n(); | ||
| const mapStore = useMapStore(); | ||
| onMounted(async () => { | ||
| if (mapStore.status === 'idle') { | ||
| await mapStore.fetchMapOverview(); | ||
| } | ||
| }); | ||
| const completedStop = computed(() => { | ||
| const slug = route.params.slug as string; | ||
| return mapStore.stops.find(s => s.slug === slug); | ||
| }); | ||
| const completedStopName = computed(() => { | ||
| return completedStop.value?.title || t('common.stop'); | ||
| }); | ||
| </script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| <template> | ||
| <AppCelebration> | ||
| <div class="flex flex-col items-center justify-center min-h-screen text-center p-6 bg-white"> | ||
| <div class="mb-8 p-4 bg-yellow-100 rounded-full"> | ||
| <span class="text-6xl">🏆</span> | ||
| </div> | ||
|
|
||
| <h1 class="text-4xl font-bold text-gray-900 mb-2"> | ||
| {{ t('success.stop_completed_title') }} | ||
| </h1> | ||
|
|
||
| <p class="text-2xl text-gray-600 mb-8"> | ||
| {{ completedStopName }} | ||
| </p> | ||
|
|
||
| <div v-if="nextStop" class="mb-12 p-6 bg-blue-50 rounded-2xl border border-blue-100 flex flex-col items-center"> | ||
| <p class="text-sm uppercase tracking-widest text-blue-500 font-bold mb-1"> | ||
| {{ t('success.next_stop_label') }} | ||
| </p> | ||
| <p class="text-xl font-semibold text-blue-900 mb-4"> | ||
| {{ nextStop.title }} | ||
| </p> | ||
| <RouterLink | ||
| :to="{ name: 'pupil-stop', params: { slug: nextStop.slug } }" | ||
| class="px-6 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors" | ||
| > | ||
| {{ t('success.next_stop') }} | ||
| </RouterLink> | ||
| </div> | ||
|
|
||
| <RouterLink | ||
| :to="{ name: 'pupil-map' }" | ||
| class="px-8 py-3 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition-colors" | ||
| > | ||
| {{ t('success.return_to_map') }} | ||
| </RouterLink> | ||
| </div> | ||
| </AppCelebration> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { computed, onMounted } from 'vue'; | ||
| import { useRoute } from 'vue-router'; | ||
| import { useI18n } from 'vue-i18n'; | ||
| import { useMapStore } from '@/map/model/map.store'; | ||
| import AppCelebration from '@/shared/components/AppCelebration.vue'; | ||
| const route = useRoute(); | ||
| const { t } = useI18n(); | ||
| const mapStore = useMapStore(); | ||
| onMounted(async () => { | ||
| if (mapStore.status === 'idle') { | ||
| await mapStore.fetchMapOverview(); | ||
| } | ||
| }); | ||
| const completedStop = computed(() => { | ||
| const slug = route.params.slug as string; | ||
| return mapStore.stops.find(s => s.slug === slug); | ||
| }); | ||
| const completedStopName = computed(() => { | ||
| return completedStop.value?.title || t('common.stop'); | ||
| }); | ||
| const nextStop = computed(() => { | ||
| const current = completedStop.value; | ||
| if (!current) return null; | ||
| return mapStore.stops | ||
| .filter(s => s.displayOrder > current.displayOrder) | ||
| .sort((a, b) => a.displayOrder - b.displayOrder)[0]; | ||
| }); | ||
| </script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,31 @@ | ||
| import type { RouteRecordRaw } from 'vue-router' | ||
|
|
||
| import MapPage from '@/map/pages/MapPage.vue' | ||
| import StopSuccessPage from '@/map/pages/StopSuccessPage.vue' | ||
| import StopFailurePage from '@/map/pages/StopFailurePage.vue' | ||
| import StopPage from '@/stops/pages/StopPage.vue' | ||
|
|
||
| export const pupilMapRoutes: RouteRecordRaw[] = [ | ||
| { | ||
| path: 'map', | ||
| name: 'pupil-map', | ||
| component: MapPage, | ||
| }, | ||
| { | ||
| path: 'map/:slug', | ||
| name: 'pupil-stop', | ||
| component: StopPage, | ||
| }, | ||
| ] | ||
| export function createMapRoutes(prefix: string): RouteRecordRaw[] { | ||
| return [ | ||
| { | ||
| path: 'map', | ||
| name: `${prefix}-map`, | ||
| component: MapPage, | ||
| }, | ||
| { | ||
| path: 'map/:slug', | ||
| name: `${prefix}-stop`, | ||
| component: StopPage, | ||
| }, | ||
| { | ||
| path: 'map/:slug/success', | ||
| name: `${prefix}-stop-success`, | ||
| component: StopSuccessPage, | ||
| }, | ||
| { | ||
| path: 'map/:slug/failure', | ||
| name: `${prefix}-stop-failure`, | ||
| component: StopFailurePage, | ||
| }, | ||
| ]; | ||
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <template> | ||
| <div class="celebration-container"> | ||
| <slot></slot> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import confetti from 'canvas-confetti'; | ||
| import { onMounted } from 'vue'; | ||
| defineProps<{ | ||
| trigger?: boolean; | ||
| }>(); | ||
| const launchConfetti = () => { | ||
| const duration = 3 * 1000; | ||
| const animationEnd = Date.now() + duration; | ||
| const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 }; | ||
| const randomInRange = (min: number, max: number) => Math.random() * (max - min) + min; | ||
| const interval: ReturnType<typeof setInterval> = setInterval(function() { | ||
| const timeLeft = animationEnd - Date.now(); | ||
| if (timeLeft <= 0) { | ||
| return clearInterval(interval); | ||
| } | ||
| const particleCount = 50 * (timeLeft / duration); | ||
| confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } }); | ||
| confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } }); | ||
| }, 250); | ||
| }; | ||
| onMounted(() => { | ||
| launchConfetti(); | ||
| }); | ||
| </script> | ||
|
|
||
| <style scoped> | ||
| .celebration-container { | ||
| position: relative; | ||
| width: 100%; | ||
| height: 100%; | ||
| } | ||
| </style> |
Oops, something went wrong.