feat: tan stack theme query
This commit is contained in:
parent
47a522e443
commit
2b509774b8
6 changed files with 197 additions and 64 deletions
|
@ -23,6 +23,12 @@ export function getThemes(req: Request, res: Response) {
|
|||
|
||||
export function getThemeByTitle(req: Request, res: Response) {
|
||||
const themeKey = req.params.theme;
|
||||
|
||||
if (!theme) {
|
||||
res.status(400).json({ error: 'Missing required field: theme' });
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = themes.find((t) => t.title === themeKey);
|
||||
|
||||
if (theme) {
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@tanstack/vue-query": "^5.69.0",
|
||||
"axios": "^1.8.2",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.2",
|
||||
"vue-router": "^4.5.0",
|
||||
"vuetify": "^3.7.12",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"axios": "^1.8.2"
|
||||
"vuetify": "^3.7.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
|
|
|
@ -1,76 +1,52 @@
|
|||
<script setup lang="ts">
|
||||
import ThemeCard from "@/components/ThemeCard.vue";
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { ref, watchEffect, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { AGE_TO_THEMES, THEMESITEMS } from "@/utils/constants.ts";
|
||||
import {getAllThemes} from "@/controllers/themes.ts";
|
||||
import {getThemeController} from "@/controllers/controllers.ts";
|
||||
import { useThemeQuery } from "@/queries/themes.ts";
|
||||
|
||||
// Receive the selectedTheme and selectedAge from the parent component
|
||||
const props = defineProps({
|
||||
selectedTheme: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
selectedAge: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
selectedTheme: { type: String, required: true },
|
||||
selectedAge: { type: String, required: true }
|
||||
});
|
||||
|
||||
const themeController = getThemeController();
|
||||
|
||||
const { locale } = useI18n();
|
||||
const language = computed(() => locale.value);
|
||||
|
||||
const allCards = ref<Array<{ key: string; title: string; description: string; image: string }>>([]);
|
||||
const cards = ref<Array<{ key: string; title: string; description: string; image: string }>>([]);
|
||||
const { data: allThemes, isLoading, error } = useThemeQuery(language);
|
||||
|
||||
// Fetch all themes based on the current language
|
||||
async function fetchThemes() {
|
||||
try {
|
||||
// Get the current selected language
|
||||
const language = locale.value;
|
||||
const allCards = ref([]);
|
||||
const cards = ref([]);
|
||||
|
||||
// Update the cards value with the fetched themes
|
||||
allCards.value = await themeController.getAll(language);
|
||||
cards.value = allCards.value;
|
||||
} catch (error) {
|
||||
console.error("Error fetching themes:", error);
|
||||
}
|
||||
}
|
||||
watchEffect(() => {
|
||||
const themes = allThemes.value ?? [];
|
||||
allCards.value = themes;
|
||||
|
||||
// Fetch on mount
|
||||
onMounted(fetchThemes);
|
||||
|
||||
// Re-fetch themes when language changes
|
||||
watch(locale, () => {
|
||||
fetchThemes();
|
||||
});
|
||||
|
||||
// Watch for selectedTheme change and filter themes
|
||||
watch(() => props.selectedTheme, (newTheme) => {
|
||||
if (newTheme) {
|
||||
cards.value = allCards.value.filter(theme => THEMESITEMS[newTheme].includes(theme.key) && AGE_TO_THEMES[props.selectedAge]?.includes(theme.key));
|
||||
if (props.selectedTheme) {
|
||||
cards.value = themes.filter((theme) =>
|
||||
THEMESITEMS[props.selectedTheme]?.includes(theme.key) &&
|
||||
AGE_TO_THEMES[props.selectedAge]?.includes(theme.key)
|
||||
);
|
||||
} else {
|
||||
cards.value = allCards.value;
|
||||
cards.value = themes;
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for selectedAge change and filter themes
|
||||
watch(() => props.selectedAge, (newAge) => {
|
||||
if (newAge) {
|
||||
cards.value = allCards.value.filter(theme => THEMESITEMS[props.selectedTheme].includes(theme.key) && AGE_TO_THEMES[newAge]?.includes(theme.key));
|
||||
} else {
|
||||
cards.value = allCards.value;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<div v-if="isLoading" class="text-center py-10">
|
||||
<v-progress-circular indeterminate color="primary" />
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="text-center py-10 text-error">
|
||||
<v-icon large>mdi-alert-circle</v-icon>
|
||||
<p>Error loading: {{ error.message }}</p>
|
||||
</div>
|
||||
|
||||
<v-row v-else>
|
||||
<v-col
|
||||
v-for="card in cards"
|
||||
:key="card.key"
|
||||
|
|
|
@ -10,6 +10,7 @@ import i18n from "./i18n/i18n.ts";
|
|||
// Components
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import { VueQueryPlugin, QueryClient } from '@tanstack/vue-query';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
|
@ -24,6 +25,18 @@ const vuetify = createVuetify({
|
|||
components,
|
||||
directives,
|
||||
});
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.use(vuetify);
|
||||
app.use(i18n);
|
||||
app.use(VueQueryPlugin, { queryClient });
|
||||
|
||||
app.mount("#app");
|
||||
|
|
25
frontend/src/queries/themes.ts
Normal file
25
frontend/src/queries/themes.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { useQuery } from '@tanstack/vue-query';
|
||||
import { getThemeController } from '@/controllers/controllers';
|
||||
import {type MaybeRefOrGetter, toValue} from "vue";
|
||||
|
||||
const themeController = getThemeController();
|
||||
|
||||
export const useThemeQuery = (language: MaybeRefOrGetter<string>) => {
|
||||
return useQuery({
|
||||
queryKey: ['themes', language],
|
||||
queryFn: () => {
|
||||
const lang = toValue(language);
|
||||
return themeController.getAll(lang);
|
||||
},
|
||||
enabled: () => !!toValue(language),
|
||||
});
|
||||
};
|
||||
|
||||
export const useThemeHruidsQuery = (themeKey: string | null) => {
|
||||
return useQuery({
|
||||
queryKey: ['theme-hruids', themeKey],
|
||||
queryFn: () => themeController.getHruidsByKey(themeKey!),
|
||||
enabled: !!themeKey,
|
||||
});
|
||||
};
|
||||
|
111
package-lock.json
generated
111
package-lock.json
generated
|
@ -91,6 +91,8 @@
|
|||
"name": "dwengo-1-frontend",
|
||||
"version": "0.1.1",
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@tanstack/vue-query": "^5.69.0",
|
||||
"axios": "^1.8.2",
|
||||
"oidc-client-ts": "^3.1.0",
|
||||
"vue": "^3.5.13",
|
||||
|
@ -2532,6 +2534,99 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/match-sorter-utils": {
|
||||
"version": "8.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz",
|
||||
"integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"remove-accents": "0.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.69.0.tgz",
|
||||
"integrity": "sha512-Kn410jq6vs1P8Nm+ZsRj9H+U3C0kjuEkYLxbiCyn3MDEiYor1j2DGVULqAz62SLZtUZ/e9Xt6xMXiJ3NJ65WyQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.69.0.tgz",
|
||||
"integrity": "sha512-Ift3IUNQqTcaFa1AiIQ7WCb/PPy8aexZdq9pZWLXhfLcLxH0+PZqJ2xFImxCpdDZrFRZhLJrh76geevS5xjRhA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.69.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/vue-query": {
|
||||
"version": "5.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/vue-query/-/vue-query-5.69.0.tgz",
|
||||
"integrity": "sha512-JZecDd0b+hZChqV5O1wXBfKoxlEj3aGvRj7upuqWei+oGrT+ERuOU4uQn7/DDVA5TouIt88G3oMFBjE2wKO/6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||
"@tanstack/query-core": "5.69.0",
|
||||
"@vue/devtools-api": "^6.6.3",
|
||||
"vue-demi": "^0.14.10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.1.2",
|
||||
"vue": "^2.6.0 || ^3.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/vue-query/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
@ -9046,6 +9141,16 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-json-fast": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz",
|
||||
|
@ -9092,6 +9197,12 @@
|
|||
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/remove-accents": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
||||
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue