|
@@ -2,24 +2,34 @@
|
|
|
<div>
|
|
<div>
|
|
|
<v-expansion-panel
|
|
<v-expansion-panel
|
|
|
class="my-1"
|
|
class="my-1"
|
|
|
- @update:model-value="fetchSongs"
|
|
|
|
|
|
|
+ @group:selected="fetchSongs"
|
|
|
>
|
|
>
|
|
|
- <v-expansion-panel-title>
|
|
|
|
|
|
|
+ <v-expansion-panel-title class="font-weight-bold">
|
|
|
{{ author.name }}
|
|
{{ author.name }}
|
|
|
</v-expansion-panel-title>
|
|
</v-expansion-panel-title>
|
|
|
|
|
+
|
|
|
<v-expansion-panel-text>
|
|
<v-expansion-panel-text>
|
|
|
<v-card v-if="loadingSongs" class="text-center pa-4">
|
|
<v-card v-if="loadingSongs" class="text-center pa-4">
|
|
|
<v-progress-circular :indeterminate="true" />
|
|
<v-progress-circular :indeterminate="true" />
|
|
|
- <div class="mt-2">Loading songs...</div>
|
|
|
|
|
|
|
+ <div class="mt-2">
|
|
|
|
|
+ Loading songs...
|
|
|
|
|
+ </div>
|
|
|
</v-card>
|
|
</v-card>
|
|
|
|
|
+
|
|
|
<v-card v-else-if="songError" class="text-center pa-4 error--text">
|
|
<v-card v-else-if="songError" class="text-center pa-4 error--text">
|
|
|
<v-icon color="error" large>mdi-alert-circle</v-icon>
|
|
<v-icon color="error" large>mdi-alert-circle</v-icon>
|
|
|
- <div class="mt-2">Error loading songs: {{ songError }}</div>
|
|
|
|
|
|
|
+ <div class="mt-2">
|
|
|
|
|
+ Error loading songs: {{ songError }}
|
|
|
|
|
+ </div>
|
|
|
</v-card>
|
|
</v-card>
|
|
|
|
|
+
|
|
|
<v-list v-else>
|
|
<v-list v-else>
|
|
|
<v-list-item v-for="song in songs" :key="song.id">
|
|
<v-list-item v-for="song in songs" :key="song.id">
|
|
|
- <v-list-item-title>{{ song.title }}</v-list-item-title>
|
|
|
|
|
|
|
+ <v-list-item-title>
|
|
|
|
|
+ - {{ song.title }}
|
|
|
|
|
+ </v-list-item-title>
|
|
|
</v-list-item>
|
|
</v-list-item>
|
|
|
|
|
+
|
|
|
<v-list-item v-if="songs.length === 0">
|
|
<v-list-item v-if="songs.length === 0">
|
|
|
<v-list-item-title class="text-center">No songs found for this author</v-list-item-title>
|
|
<v-list-item-title class="text-center">No songs found for this author</v-list-item-title>
|
|
|
</v-list-item>
|
|
</v-list-item>
|
|
@@ -30,59 +40,53 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, watch } from 'vue'
|
|
|
|
|
|
|
+import { ref, PropType } from 'vue'
|
|
|
|
|
+import type Author from '~/types/interfaces'
|
|
|
|
|
|
|
|
// Define props
|
|
// Define props
|
|
|
-const props = defineProps<{
|
|
|
|
|
|
|
+const props = defineProps({
|
|
|
author: {
|
|
author: {
|
|
|
- id: number;
|
|
|
|
|
- name: string;
|
|
|
|
|
- };
|
|
|
|
|
-}>();
|
|
|
|
|
|
|
+ type: Object as PropType<Author>,
|
|
|
|
|
+ required: true
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
-// Define types
|
|
|
|
|
-interface Song {
|
|
|
|
|
- id: number;
|
|
|
|
|
- title: string;
|
|
|
|
|
- author: {
|
|
|
|
|
- id: number;
|
|
|
|
|
- name: string;
|
|
|
|
|
- } | string; // Can be either an object or an IRI string
|
|
|
|
|
-}
|
|
|
|
|
|
|
+const runtimeConfig = useRuntimeConfig()
|
|
|
|
|
+
|
|
|
|
|
+const apiBaseUrl = runtimeConfig.apiBaseUrl ?? runtimeConfig.public.apiBaseUrl
|
|
|
|
|
|
|
|
-// State for songs
|
|
|
|
|
const songs = ref<Song[]>([]);
|
|
const songs = ref<Song[]>([]);
|
|
|
const loadingSongs = ref(false);
|
|
const loadingSongs = ref(false);
|
|
|
const songError = ref<string | null>(null);
|
|
const songError = ref<string | null>(null);
|
|
|
|
|
|
|
|
// Fetch songs for the author
|
|
// Fetch songs for the author
|
|
|
const fetchSongs = async () => {
|
|
const fetchSongs = async () => {
|
|
|
- console.log('fetchSongs', props.author.id);
|
|
|
|
|
|
|
+ console.log('fetchSongs', props.author.id)
|
|
|
|
|
|
|
|
// Skip if already loaded
|
|
// Skip if already loaded
|
|
|
if (songs.value.length > 0) {
|
|
if (songs.value.length > 0) {
|
|
|
- return;
|
|
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- loadingSongs.value = true;
|
|
|
|
|
|
|
+ loadingSongs.value = true
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- const response = await fetch(`https://local.api.snc-demo.fr/api/songs?author=${props.author.id}`);
|
|
|
|
|
|
|
+ const response = await fetch( apiBaseUrl + `/api/songs?author=${props.author.id}`)
|
|
|
if (!response.ok) {
|
|
if (!response.ok) {
|
|
|
- throw new Error(`HTTP error. Status: ${response.status}`);
|
|
|
|
|
|
|
+ throw new Error(`HTTP error. Status: ${response.status}`)
|
|
|
}
|
|
}
|
|
|
- const data = await response.json();
|
|
|
|
|
- songs.value = data['member'] || [];
|
|
|
|
|
|
|
+ const data = await response.json()
|
|
|
|
|
+ songs.value = data.member || []
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
songError.value = err instanceof Error ? err.message : 'Unknown error';
|
|
songError.value = err instanceof Error ? err.message : 'Unknown error';
|
|
|
- console.error(`Error fetching songs for author ${props.author.id}:`, err);
|
|
|
|
|
|
|
+ console.error(`Error fetching songs for author ${props.author.id}:`, err)
|
|
|
} finally {
|
|
} finally {
|
|
|
- loadingSongs.value = false;
|
|
|
|
|
|
|
+ loadingSongs.value = false
|
|
|
}
|
|
}
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// Expose methods to parent component
|
|
// Expose methods to parent component
|
|
|
defineExpose({
|
|
defineExpose({
|
|
|
fetchSongs
|
|
fetchSongs
|
|
|
-});
|
|
|
|
|
|
|
+})
|
|
|
</script>
|
|
</script>
|