v.0.1.0 pinia, albums-store
This commit is contained in:
Generated
+44
-7
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "tmp-app-creation",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tmp-app-creation",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.6",
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/axios": "^0.9.36",
|
||||
"axios": "^1.20.0",
|
||||
"pinia": "^4.0.3",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"vue": "^3.5.42"
|
||||
},
|
||||
@@ -1469,6 +1470,16 @@
|
||||
"@vue/shared": "3.5.42"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz",
|
||||
"integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/devtools-kit": "^8.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-core": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.2.1.tgz",
|
||||
@@ -1487,7 +1498,6 @@
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz",
|
||||
"integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-shared": "^8.2.1",
|
||||
@@ -1500,7 +1510,6 @@
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz",
|
||||
"integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/language-core": {
|
||||
@@ -1663,7 +1672,6 @@
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
|
||||
"integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
@@ -2195,7 +2203,6 @@
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
@@ -2702,6 +2709,12 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/nostics": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nostics/-/nostics-1.2.0.tgz",
|
||||
"integrity": "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/npm-normalize-package-bin": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-6.0.0.tgz",
|
||||
@@ -2809,7 +2822,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
|
||||
"integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
@@ -2843,6 +2855,31 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-4.0.3.tgz",
|
||||
"integrity": "sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nostics": "^1.1.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/devtools-api": "^8.1.5",
|
||||
"typescript": ">=5.6.0",
|
||||
"vue": "^3.5.11"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/devtools-api": {
|
||||
"optional": false
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.28",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/axios": "^0.9.36",
|
||||
"axios": "^1.20.0",
|
||||
"pinia": "^4.0.3",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"vue": "^3.5.42"
|
||||
},
|
||||
|
||||
+4
-2
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import FirstVueComponent from "./components/FirstVueComponent.vue";
|
||||
import SecondVueComponent from "./components/SecondVueComponent.vue";
|
||||
import FetchFromApi from "./components/FetchFromApi.vue";
|
||||
|
||||
import pkg from "../package.json"; // Works out-of-the-box in Vite
|
||||
|
||||
import pool_bg from "@/assets/images/swimming-pool.jpg";
|
||||
import type { Album } from "./types/album.ts";
|
||||
import AlbumsList from "./components/AlbumsList.vue";
|
||||
import AlbumTitles from "./components/AlbumTitles.vue";
|
||||
|
||||
document.title = `${pkg.name} ${pkg.version}©2026`;
|
||||
|
||||
@@ -25,7 +26,8 @@ const sample_album: Album = {
|
||||
<div class="container">
|
||||
<FirstVueComponent class="component-box-fade" />
|
||||
<SecondVueComponent :album="sample_album" />
|
||||
<FetchFromApi class="component-box-fade" />
|
||||
<AlbumsList />
|
||||
<AlbumTitles />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<!-- src/components/AlbumTitles.vue -->
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAlbumsStore } from "@/stores/albums";
|
||||
|
||||
const { titles, count } = storeToRefs(useAlbumsStore());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ count }} albums loaded</p>
|
||||
<ul>
|
||||
<li v-for="t in titles" :key="t">{{ t }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!-- src/components/AlbumsList.vue -->
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAlbumsStore } from "@/stores/albums";
|
||||
import AlbumCard from "@/components/AlbumCard.vue";
|
||||
|
||||
const store = useAlbumsStore();
|
||||
|
||||
// ⚠️ storeToRefs keeps reactivity for state + getters
|
||||
const { albums, loading, error } = storeToRefs(store);
|
||||
// actions can be destructured directly — they're bound
|
||||
const { fetchAlbums } = store;
|
||||
|
||||
onMounted(() => fetchAlbums());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="m-2.5 w-1/3 rounded-2xl bg-linear-to-br from-emerald-200 to-emerald-500 border-2"
|
||||
>
|
||||
<div v-if="loading" class="p-3">Loading…</div>
|
||||
<div v-else-if="error" class="p-3 text-red-700">Failed to load albums.</div>
|
||||
<div v-else class="p-1.5 flex flex-wrap gap-3">
|
||||
<AlbumCard v-for="a in albums" :key="a.id" :album="a" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,47 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import axios from "axios";
|
||||
import type { Album } from "@/types/album";
|
||||
|
||||
const album_titles = ref<string[]>([]);
|
||||
|
||||
onMounted(async (): Promise<void> => {
|
||||
try {
|
||||
// Axios automatically parses the JSON response into 'response.data'
|
||||
const albums_response = await axios.get<Album[]>(
|
||||
"https://jsonplaceholder.typicode.com/albums",
|
||||
);
|
||||
|
||||
console.log("albums_response:", albums_response.data);
|
||||
|
||||
album_titles.value = albums_response.data
|
||||
.slice(0, 12)
|
||||
.map((album) => album.title);
|
||||
|
||||
console.log("album_titles:", [...album_titles.value]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching albums!", error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<h3>Albums List</h3>
|
||||
<ul>
|
||||
<li v-for="(album_title, index) in album_titles" :key="index">
|
||||
{{ album_title }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Optional styling for your card */
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -4,13 +4,20 @@ import { ref } from "vue";
|
||||
const msg = ref<string>("Tom is building a vue template, oh yeah");
|
||||
const status = ref<boolean>(true);
|
||||
|
||||
defineProps({
|
||||
hide: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Define function directly
|
||||
const toggleStatus = (): void => {
|
||||
status.value = !status.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="hide">
|
||||
<div class="card">
|
||||
<div class="tj">{{ msg }} {{ status }}</div>
|
||||
<p v-if="status">{{ msg }} {{ status }}</p>
|
||||
|
||||
+4
-1
@@ -1,5 +1,8 @@
|
||||
import { createPinia } from "pinia";
|
||||
import { createApp } from "vue";
|
||||
import App from "@/App.vue";
|
||||
import "@/assets/styles/global.css";
|
||||
|
||||
createApp(App).mount("#app");
|
||||
const app = createApp(App);
|
||||
app.use(createPinia());
|
||||
app.mount("#app");
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// src/stores/albums.ts
|
||||
import { ref, computed } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import axios from "axios";
|
||||
import type { Album } from "@/types/album";
|
||||
|
||||
const API_URL = "https://jsonplaceholder.typicode.com/albums";
|
||||
|
||||
export const useAlbumsStore = defineStore("albums", () => {
|
||||
// ---- state ----
|
||||
const albums = ref<Album[]>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref<unknown>(null);
|
||||
let loaded = false; // module-internal guard (not reactive)
|
||||
|
||||
// ---- getters ----
|
||||
const titles = computed(() => albums.value.map((a) => a.title));
|
||||
const count = computed(() => albums.value.length);
|
||||
const isLoading = computed(() => loading.value);
|
||||
|
||||
// ---- actions ----
|
||||
async function fetchAlbums(force = false) {
|
||||
if (loaded && !force) return;
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const { data } = await axios.get<Album[]>(API_URL);
|
||||
albums.value = data.slice(0, 12);
|
||||
loaded = true;
|
||||
console.log("albums loaded:", albums.value.length);
|
||||
} catch (e) {
|
||||
error.value = e;
|
||||
console.error("Error fetching albums!", e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
albums.value = [];
|
||||
error.value = null;
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
return {
|
||||
albums,
|
||||
loading,
|
||||
error,
|
||||
titles,
|
||||
count,
|
||||
isLoading,
|
||||
fetchAlbums,
|
||||
reset,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user