diff --git a/app/composables/useProjects.ts b/app/composables/useProjects.ts new file mode 100644 index 0000000..27aacac --- /dev/null +++ b/app/composables/useProjects.ts @@ -0,0 +1,51 @@ +import { projects as projectsData } from '~/data/projects' +import type { Project } from '~~/shared/types' + +/** + * Composable for accessing and filtering project data with i18n support. + * Titles, descriptions, and long descriptions are resolved via i18n keys. + */ +export function useProjects() { + const { t } = useI18n() + + const projects = computed(() => + projectsData.map((p) => ({ + ...p, + title: t(`projects.${p.id}.title`), + description: t(`projects.${p.id}.description`), + longDescription: t(`projects.${p.id}.longDescription`) || undefined, + })), + ) + + const featuredProjects = computed(() => projects.value.filter((p) => p.featured)) + + function filterByCategory(category: string) { + return computed(() => projects.value.filter((p) => p.category === category)) + } + + function search(query: Ref | string) { + return computed(() => { + const q = typeof query === 'string' ? query : query.value + if (!q) return projects.value + const lower = q.toLowerCase() + return projects.value.filter( + (p) => + p.title.toLowerCase().includes(lower) || + p.description.toLowerCase().includes(lower) || + p.technologies.some((tech) => tech.toLowerCase().includes(lower)), + ) + }) + } + + function findById(id: string) { + return computed(() => projects.value.find((p) => p.id === id)) + } + + return { + projects, + featuredProjects, + filterByCategory, + search, + findById, + } +} diff --git a/package.json b/package.json index 6f487da..cf1fc4e 100644 --- a/package.json +++ b/package.json @@ -4,41 +4,35 @@ "private": true, "type": "module", "scripts": { - "dev": "vite", - "build": "run-p type-check \"build-only {@}\" --", - "preview": "vite preview", - "build-only": "vite build", - "type-check": "vue-tsc --build", - "lint": "eslint . --fix", - "format": "prettier --write src/" + "dev": "nuxt dev", + "build": "nuxt build", + "generate": "nuxt generate", + "preview": "nuxt preview", + "postinstall": "nuxt prepare", + "lint": "eslint .", + "typecheck": "nuxt typecheck" }, "dependencies": { - "@vueuse/head": "^2.0.0", - "pinia": "^3.0.1", - "vue": "^3.5.13", - "vue-i18n": "^9.14.4", - "vue-router": "^4.5.0" + "@nuxt/eslint": "^1.15.2", + "@nuxt/image": "^2.0.0", + "@nuxt/ui": "^3.0.0", + "@nuxtjs/i18n": "^10.2.4", + "@nuxtjs/sitemap": "^8.0.12", + "nuxt": "^4.0.0", + "nuxt-gtag": "^4.1.0", + "vue": "latest", + "vue-router": "latest" }, "devDependencies": { - "@tailwindcss/postcss": "^4.1.10", - "@tsconfig/node22": "^22.0.1", - "@types/node": "^22.14.0", - "@vitejs/plugin-vue": "^5.2.3", - "@vue/eslint-config-prettier": "^10.2.0", - "@vue/eslint-config-typescript": "^14.5.0", - "@vue/tsconfig": "^0.7.0", - "autoprefixer": "^10.4.21", - "eslint": "^9.22.0", - "eslint-plugin-vue": "~10.0.0", - "jiti": "^2.4.2", - "npm-run-all2": "^7.0.2", - "postcss": "^8.5.6", - "prettier": "3.5.3", - "tailwindcss": "^4.1.10", - "terser": "^5.43.1", - "typescript": "~5.8.0", - "vite": "^6.2.4", - "vite-plugin-vue-devtools": "^7.7.2", - "vue-tsc": "^2.2.8" + "typescript": "~5.8.0" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "@parcel/watcher", + "esbuild", + "sharp", + "unrs-resolver", + "vue-demi" + ] } } diff --git a/shared/types/index.ts b/shared/types/index.ts index 64685bc..4143f5e 100644 --- a/shared/types/index.ts +++ b/shared/types/index.ts @@ -5,6 +5,9 @@ export interface ProjectButton { export interface Project { id: string + title: string + description: string + longDescription?: string image: string technologies: string[] category: string