@ozdao/martyrs 0.2.493 → 0.2.495
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index.cjs +4 -4
- package/dist/_virtual/index.js +4 -4
- package/dist/_virtual/index2.cjs +4 -4
- package/dist/_virtual/index2.js +4 -4
- package/dist/builder.cjs +43 -88
- package/dist/builder.js +45 -90
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue.cjs +1 -1
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue.js +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/globals.client.cjs +1 -1
- package/dist/martyrs/src/modules/globals/globals.client.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/globals.client.js +1 -1
- package/dist/martyrs/src/modules/globals/globals.client.js.map +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.js +1 -1
- package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs +28 -13
- package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js +28 -13
- package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js.map +1 -1
- package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.js +1 -1
- package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs +1 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs +2 -2
- package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js +2 -2
- package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs +86 -81
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +88 -83
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs +4 -4
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js +4 -4
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs +5 -5
- package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.js +5 -5
- package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs +12 -9
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +12 -9
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.cjs +7 -7
- package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js +7 -7
- package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs +43 -37
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +45 -39
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/music.client.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/music.client.js.map +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.cjs +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.cjs.map +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.js +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.js.map +1 -1
- package/dist/style.css +5 -5
- package/package.json +1 -1
- package/src/builder/rspack/rspack.config.spa.client.js +0 -44
- package/src/builder/rspack/rspack.config.ssr.client.js +41 -41
- package/src/modules/globals/globals.client.js +1 -2
- package/src/modules/globals/views/components/layouts/Client.vue +13 -11
- package/src/modules/globals/views/router/scrollBehavior.js +1 -1
- package/src/modules/music/components/SidebarMusic.vue +7 -7
- package/src/modules/music/components/cards/TrackListCard.vue +1 -1
- package/src/modules/music/components/forms/SearchForm.vue +1 -1
- package/src/modules/music/components/pages/Album.vue +26 -29
- package/src/modules/music/components/pages/Artist.vue +4 -4
- package/src/modules/music/components/pages/MusicLibrary.vue +5 -5
- package/src/modules/music/components/pages/Playlist.vue +9 -9
- package/src/modules/music/components/pages/SearchResults.vue +6 -6
- package/src/modules/music/components/pages/Track.vue +22 -20
- package/src/modules/music/music.client.js +1 -1
- package/src/modules/notifications/notifications.client.js +1 -1
|
@@ -50,7 +50,7 @@ const _hoisted_14 = {
|
|
|
50
50
|
};
|
|
51
51
|
const _hoisted_15 = { class: "h1 mn-b-medium" };
|
|
52
52
|
const _hoisted_16 = { class: "flex gap-small mn-b-medium" };
|
|
53
|
-
const _hoisted_17 = { class: "dropdown-menu bg-
|
|
53
|
+
const _hoisted_17 = { class: "dropdown-menu bg-white pd-small radius-medium shadow-big mn-t-thin" };
|
|
54
54
|
const _hoisted_18 = { class: "owner-section mn-b-big" };
|
|
55
55
|
const _hoisted_19 = { class: "owner-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
56
56
|
const _hoisted_20 = { class: "owner-avatar" };
|
|
@@ -346,10 +346,13 @@ const _sfc_main = {
|
|
|
346
346
|
onClick: playPlaylist,
|
|
347
347
|
color: "primary",
|
|
348
348
|
size: "medium",
|
|
349
|
-
class: "flex-1 flex-center gap-thin"
|
|
349
|
+
class: "flex-1 t-white bg-black radius-thin flex-center gap-thin"
|
|
350
350
|
}, {
|
|
351
351
|
default: withCtx(() => [
|
|
352
|
-
createVNode(_sfc_main$2, {
|
|
352
|
+
createVNode(_sfc_main$2, {
|
|
353
|
+
fill: "rgb(var(--white))",
|
|
354
|
+
class: "i-medium"
|
|
355
|
+
}),
|
|
353
356
|
_cache[11] || (_cache[11] = createTextVNode(" Play All "))
|
|
354
357
|
]),
|
|
355
358
|
_: 1
|
|
@@ -358,7 +361,7 @@ const _sfc_main = {
|
|
|
358
361
|
onClick: shufflePlay,
|
|
359
362
|
color: "primary",
|
|
360
363
|
size: "medium",
|
|
361
|
-
class: "flex-1 flex-center gap-thin"
|
|
364
|
+
class: "flex-1 bg-light radius-thin flex-center gap-thin"
|
|
362
365
|
}, {
|
|
363
366
|
default: withCtx(() => [
|
|
364
367
|
createVNode(_sfc_main$3, { class: "i-medium" }),
|
|
@@ -370,7 +373,7 @@ const _sfc_main = {
|
|
|
370
373
|
onClick: toggleFollow,
|
|
371
374
|
color: "primary",
|
|
372
375
|
size: "medium",
|
|
373
|
-
class: "flex-1 flex-center gap-thin"
|
|
376
|
+
class: "flex-1 bg-light radius-thin flex-center gap-thin"
|
|
374
377
|
}, {
|
|
375
378
|
default: withCtx(() => [
|
|
376
379
|
createTextVNode(toDisplayString(isFollowing.value ? "Follow" : "Unfollow"), 1)
|
|
@@ -378,7 +381,7 @@ const _sfc_main = {
|
|
|
378
381
|
_: 1
|
|
379
382
|
}),
|
|
380
383
|
createVNode(_sfc_main$4, {
|
|
381
|
-
label: { component: _sfc_main$5, class: "i-
|
|
384
|
+
label: { component: _sfc_main$5, class: "bg-light radius-thin pd-thin i-big" },
|
|
382
385
|
modelValue: showDropdown.value,
|
|
383
386
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => showDropdown.value = $event),
|
|
384
387
|
class: "relative"
|
|
@@ -515,7 +518,7 @@ const _sfc_main = {
|
|
|
515
518
|
collaborator.photoUrl ? (openBlock(), createBlock(Media, {
|
|
516
519
|
key: 0,
|
|
517
520
|
url: collaborator.photoUrl,
|
|
518
|
-
class: "
|
|
521
|
+
class: "i-regular radius-full object-cover"
|
|
519
522
|
}, null, 8, ["url"])) : createCommentVNode("", true),
|
|
520
523
|
createElementVNode("span", _hoisted_27, toDisplayString(collaborator.name || collaborator.profile?.name || "User"), 1)
|
|
521
524
|
]);
|
|
@@ -651,7 +654,7 @@ const _sfc_main = {
|
|
|
651
654
|
createVNode(_sfc_main$c, {
|
|
652
655
|
isPopupOpen: showEditModal.value && (isOwner.value || isCollaborator.value),
|
|
653
656
|
onClosePopup: _cache[4] || (_cache[4] = ($event) => showEditModal.value = false),
|
|
654
|
-
class: "bg-
|
|
657
|
+
class: "bg-white pd-medium w-m-30r radius-medium"
|
|
655
658
|
}, {
|
|
656
659
|
default: withCtx(() => [
|
|
657
660
|
createVNode(_sfc_main$d, {
|
|
@@ -666,7 +669,7 @@ const _sfc_main = {
|
|
|
666
669
|
createVNode(_sfc_main$c, {
|
|
667
670
|
isPopupOpen: showDeleteModal.value,
|
|
668
671
|
onClosePopup: _cache[6] || (_cache[6] = ($event) => showDeleteModal.value = false),
|
|
669
|
-
class: "bg-
|
|
672
|
+
class: "bg-white pd-medium w-m-25r radius-medium"
|
|
670
673
|
}, {
|
|
671
674
|
default: withCtx(() => [
|
|
672
675
|
_cache[34] || (_cache[34] = createElementVNode("h3", { class: "mn-b-medium" }, "Delete Playlist", -1)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Playlist.vue.js","sources":["../../../../../../../src/modules/music/components/pages/Playlist.vue"],"sourcesContent":["<template>\n <div class=\"playlist-page pd-small\">\n <!-- Not Found -->\n <div v-if=\"hasLoaded && !playlist\" class=\"t-center pd-big\">\n <h2 class=\"\">Playlist not found</h2>\n <p class=\"t-transp t-medium\">The playlist you're looking for doesn't exist or has been removed.</p>\n </div>\n \n <!-- Playlist Content -->\n <div v-if=\"playlist\" class=\"playlist-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative playlist-cover-section\">\n <!-- Cover -->\n <div class=\"cover-container relative mn-b-medium radius-big overflow-hidden shadow-big\">\n <Media \n :url=\"playlist.coverUrl || '/assets/placeholder-playlist.jpg'\"\n :alt=\"playlist.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n </div>\n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ playlistTracks.length }}</div>\n <div class=\"t-small t-transp t-uppercase\">Tracks</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(playlist.followers || 0) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Followers</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Playlist Details -->\n <div class=\"playlist-details-section\">\n <!-- Playlist Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase\">\n Playlist\n </span>\n <span v-if=\"playlist.isCollaborative\" class=\"badge bg-secondary-transp-20 t-secondary pd-thin-big radius-small t-small\">\n Collaborative\n </span>\n <span v-if=\"playlist.status === 'published'\" class=\"badge bg-success-transp-20 t-success pd-thin-big radius-small t-small\">\n Published\n </span>\n </div>\n\n <!-- Playlist Title -->\n <h1 class=\"h1 mn-b-medium\">{{ playlist.title }}</h1>\n\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playPlaylist\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 flex-center gap-thin\"\n >\n <IconPlay class=\"i-medium\" />\n Play All\n </Button>\n\n <Button\n @click=\"shufflePlay\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 flex-center gap-thin\"\n >\n <IconShuffle class=\"i-medium\" />\n Shuffle\n </Button>\n\n <Button\n @click=\"toggleFollow\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 flex-center gap-thin\"\n >\n {{isFollowing ? 'Follow' : 'Unfollow'}}\n </Button>\n\n \n <Dropdown :label=\"{component: IconEllipsis, class: 'i-medium' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"addToQueue\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Queue\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner || isCollaborator\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editPlaylist\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Playlist\n </Button>\n <Button v-if=\"isOwner\" @click=\"toggleCollaborative\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n {{ playlist.isCollaborative ? 'Make Private' : 'Make Collaborative' }}\n </Button>\n <Button v-if=\"isOwner\" @click=\"deletePlaylist\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Playlist\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Owner/Creator Card -->\n <div class=\"owner-section mn-b-big\">\n <h3 class=\"t-medium mn-b-small\">Created by</h3>\n <div class=\"owner-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <router-link \n :to=\"getOwnerProfileLink(playlist.creator || playlist.owner)\"\n class=\"flex items-center gap-medium flex-1 hover-opacity\"\n >\n <div class=\"owner-avatar\">\n <Media \n v-if=\"getOwnerData(playlist)?.photoUrl\"\n :url=\"getOwnerData(playlist).photoUrl\"\n :alt=\"getOwnerData(playlist)?.name\"\n class=\"w-4r h-4r radius-full object-cover\"\n />\n <div v-else class=\"w-4r h-4r radius-full bg-primary flex-center \">\n {{ getPlaylistOwnerName(playlist).charAt(0) }}\n </div>\n </div>\n <div>\n <div class=\"flex items-center gap-thin\">\n <span class=\"t-large \">{{ getPlaylistOwnerName(playlist) }}</span>\n <IconVerified v-if=\"getOwnerData(playlist)?.isVerified\" class=\"w-1r h-1r t-primary\" />\n </div>\n <span class=\"t-small t-transp\">{{ playlist.creator?.type || 'User' }}</span>\n </div>\n </router-link>\n <Button \n v-if=\"!isOwner && authState.user\"\n @click=\"() => toggleFollowUser(getOwnerId(playlist))\"\n :color=\"followedUsers.includes(getOwnerId(playlist)) ? 'primary' : 'transp'\"\n size=\"small\"\n >\n {{ followedUsers.includes(getOwnerId(playlist)) ? 'Following' : 'Follow' }}\n </Button>\n </div>\n </div>\n\n <!-- Collaborators -->\n <div v-if=\"playlist.collaborators && playlist.collaborators.length > 0\" class=\"collaborators-section mn-b-big\">\n <h3 class=\"t-medium mn-b-small\">Collaborators</h3>\n <div class=\"flex flex-wrap gap-small\">\n <div \n v-for=\"collaborator in playlist.collaborators\"\n :key=\"collaborator._id || collaborator\"\n class=\"collaborator-chip bg-light pd-thin-big radius-full flex items-center gap-thin\"\n >\n <Media \n v-if=\"collaborator.photoUrl\"\n :url=\"collaborator.photoUrl\"\n class=\"w-1-5r h-1-5r radius-full object-cover\"\n />\n <span class=\"t-small\">{{ collaborator.name || collaborator.profile?.name || 'User' }}</span>\n </div>\n </div>\n </div>\n\n <!-- Metadata Cards -->\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-big\">\n <!-- Created Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCalendar class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Created</div>\n <div class=\"t-medium \">{{ formatDate(playlist.createdAt) }}</div>\n </div>\n </div>\n\n <!-- Total Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconClock class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ totalDuration }}</div>\n </div>\n </div>\n\n <!-- Updated Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconRefresh class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Updated</div>\n <div class=\"t-medium \">{{ formatDate(playlist.updatedAt) }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconEye class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ playlist.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n <!-- Tags -->\n <div v-if=\"playlist.tags && playlist.tags.length\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"tag in playlist.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"playlist.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ playlist.description }}</p>\n </div>\n </div>\n </div>\n\n <!-- Playlist Tracks -->\n <section v-if=\"!isLoading && playlist && playlistTracks.length\" class=\"tracks-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Tracklist</h2>\n <Feed\n :store=\"{\n read: () => Promise.resolve(playlistTracks),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"playlistTracks\"\n :states=\"{\n empty: {\n title: 'No tracks in playlist',\n description: 'Add some tracks to get started',\n class: 'pd-medium t-center'\n }\n }\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in items\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index + 1\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n :canRemove=\"isOwner || isCollaborator\"\n @remove=\"() => removeTrack(track._id)\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- Empty State -->\n <section v-else-if=\"!isLoading && playlist && !playlistTracks.length\" class=\"empty-section mn-t-big\">\n <div class=\"empty-tracks t-center pd-big bg-light radius-medium\">\n <h3 class=\" mn-b-small\">This playlist is empty</h3>\n <p class=\"t-transp t-medium mn-b-medium\">Add some tracks to get started</p>\n \n <Button \n v-if=\"isOwner || isCollaborator\"\n @click=\"$router.push({ name: 'music-search' })\"\n color=\"primary\"\n size=\"medium\"\n >\n Find Tracks\n </Button>\n </div>\n </section>\n\n <!-- More Playlists -->\n <section v-if=\"!isLoading && playlist && morePlaylists.length\" class=\"more-playlists-section mn-t-big\">\n <div class=\"flex justify-between items-center mn-b-medium\">\n <h2 class=\"h2\">More Playlists</h2>\n <router-link \n v-if=\"playlist.creator\"\n :to=\"getOwnerProfileLink(playlist.creator)\" \n class=\"t-primary hover-opacity\"\n >\n See all\n </router-link>\n </div>\n <div class=\"flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide\">\n <li v-for=\"relatedPlaylist in morePlaylists\" :key=\"relatedPlaylist._id\" class=\"flex-none scroll-snap-align-start\">\n <PlaylistCard :playlist=\"relatedPlaylist\" class=\"w-min-15r transition-cubic-in-out\" />\n </li>\n </div>\n </section>\n\n <!-- Edit Playlist Modal -->\n <Popup \n :isPopupOpen=\"showEditModal && (isOwner || isCollaborator)\"\n @close-popup=\"showEditModal = false\" \n class=\"bg-dark pd-medium w-m-30r radius-medium\"\n >\n <PlaylistForm \n :editMode=\"true\"\n :url=\"playlist.url\"\n @cancel=\"showEditModal = false\"\n @updated=\"handlePlaylistUpdated\"\n />\n </Popup>\n\n <!-- Delete Confirmation Modal -->\n <Popup \n :isPopupOpen=\"showDeleteModal\"\n @close-popup=\"showDeleteModal = false\" \n class=\"bg-dark pd-medium w-m-25r radius-medium\"\n >\n <h3 class=\"mn-b-medium\">Delete Playlist</h3>\n <p class=\"t-transp mn-b-medium\">Are you sure you want to delete \"{{ playlist.title }}\"? This action cannot be undone.</p>\n \n <div class=\"flex justify-end gap-small\">\n <Button \n @click=\"showDeleteModal = false\"\n color=\"transp\"\n size=\"medium\"\n >\n Cancel\n </Button>\n \n <Button \n @click=\"confirmDelete\"\n color=\"danger\"\n size=\"medium\"\n >\n Delete Playlist\n </Button>\n </div>\n </Popup>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\nimport Popup from '@martyrs/src/components/Popup/Popup.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconRefresh from '@martyrs/src/modules/icons/navigation/IconRefresh.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport PlaylistCard from '../cards/PlaylistCard.vue';\nimport PlaylistForm from '../forms/PlaylistForm.vue';\n\n// Store\nimport { state as playlistsState, actions as playlistsActions } from '../../store/playlists.js';\nimport { state as tracksState, actions as tracksActions } from '../../store/tracks.js';\nimport { actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\nimport * as globals from '@martyrs/src/modules/globals/views/store/globals.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst isFollowing = ref(false);\nconst showDropdown = ref(false);\nconst showEditModal = ref(false);\nconst showDeleteModal = ref(false);\nconst followedUsers = ref([]);\nconst morePlaylists = ref([]);\n\n// Clear state\nplaylistsState.currentPlaylist = null;\nplaylistsState.currentPlaylistTracks = [];\n\n// Computed\nconst playlist = computed(() => playlistsState.currentPlaylist);\nconst playlistTracks = computed(() => playlistsState.currentPlaylistTracks || []);\n\nconst isOwner = computed(() => {\n if (!playlist.value || !authState.user) return false;\n \n const ownerId = playlist.value.owner?.target?._id || playlist.value.owner?.target;\n return ownerId === authState.user._id;\n});\n\nconst isCollaborator = computed(() => {\n if (!playlist.value || !authState.user) return false;\n \n return playlist.value.collaborators?.some(collab => \n (collab._id || collab) === authState.user._id\n );\n});\n\nconst totalDuration = computed(() => {\n if (!playlistTracks.value.length) return '0:00';\n const totalSeconds = playlistTracks.value.reduce((sum, track) => sum + (track.duration || 0), 0);\n return formatDuration(totalSeconds);\n});\n\n// Helper functions\nconst getOwnerData = (playlist) => {\n if (!playlist) return null;\n const owner = playlist.creator?.target || playlist.owner?.target;\n return typeof owner === 'object' ? owner : null;\n};\n\nconst getOwnerId = (playlist) => {\n if (!playlist) return null;\n const owner = playlist.creator?.target || playlist.owner?.target;\n return typeof owner === 'object' ? owner._id : owner;\n};\n\nconst getPlaylistOwnerName = (playlist) => {\n if (!playlist) return 'Unknown';\n \n const owner = getOwnerData(playlist);\n if (owner) {\n return owner.profile?.name || owner.name || 'Unknown';\n }\n \n return 'Unknown';\n};\n\nconst getOwnerProfileLink = (owner) => {\n if (!owner || !owner.target) return { name: 'music-home' };\n \n const targetId = typeof owner.target === 'object' ? owner.target._id : owner.target;\n \n if (owner.type === 'user' || owner.type === 'User') {\n return { name: 'User Profile', params: { _id: targetId } };\n } else if (owner.type === 'organization' || owner.type === 'Organization') {\n return { name: 'Organizatio', params: { _id: targetId } };\n }\n \n return { name: 'music-home' };\n};\n\n// Format helpers\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playPlaylist = () => {\n if (playlistTracks.value && playlistTracks.value.length > 0) {\n playerActions.setQueue(playlistTracks.value);\n }\n};\n\nconst shufflePlay = () => {\n if (playlistTracks.value && playlistTracks.value.length > 0) {\n const shuffled = [...playlistTracks.value].sort(() => Math.random() - 0.5);\n playerActions.setQueue(shuffled);\n }\n};\n\nconst toggleFollow = async () => {\n isFollowing.value = !isFollowing.value;\n // TODO: Implement actual following\n try {\n if (isFollowing.value) {\n await playlistsActions.followPlaylist(playlist.value._id);\n } else {\n await playlistsActions.unfollowPlaylist(playlist.value._id);\n }\n } catch (error) {\n console.error('Error toggling follow:', error);\n isFollowing.value = !isFollowing.value; // Revert on error\n }\n};\n\nconst toggleFollowUser = (userId) => {\n const index = followedUsers.value.indexOf(userId);\n if (index > -1) {\n followedUsers.value.splice(index, 1);\n } else {\n followedUsers.value.push(userId);\n }\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (playlistTracks.value && playlistTracks.value.length > 0) {\n playlistTracks.value.forEach(track => {\n playerActions.addToQueue(track);\n });\n showDropdown.value = false;\n }\n};\n\nconst editPlaylist = () => {\n showEditModal.value = true;\n showDropdown.value = false;\n};\n\nconst toggleCollaborative = async () => {\n try {\n const updatedData = {\n _id: playlist.value._id,\n isCollaborative: !playlist.value.isCollaborative\n };\n \n await playlistsActions.updatePlaylist(updatedData);\n showDropdown.value = false;\n } catch (error) {\n console.error('Error updating playlist:', error);\n }\n};\n\nconst deletePlaylist = () => {\n showDeleteModal.value = true;\n showDropdown.value = false;\n};\n\nconst confirmDelete = async () => {\n try {\n await playlistsActions.deletePlaylist(playlist.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Error deleting playlist:', error);\n globals.actions.setError({\n message: 'Failed to delete playlist'\n });\n }\n};\n\nconst removeTrack = async (trackId) => {\n try {\n await playlistsActions.removeTrackFromPlaylist(playlist.value._id, trackId);\n // Refresh playlist data\n await fetchPlaylistData();\n } catch (error) {\n console.error('Error removing track:', error);\n globals.actions.setError({\n message: 'Failed to remove track'\n });\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\nconst handlePlaylistUpdated = () => {\n showEditModal.value = false;\n fetchPlaylistData();\n};\n\n// Data fetching\nconst fetchPlaylistData = async () => {\n try {\n await playlistsActions.fetchPlaylistByUrl(route.params.url);\n \n // Check if following\n if (authState.user && playlist.value) {\n // TODO: Check if user is following this playlist\n }\n \n // Fetch more playlists from the same creator\n if (playlist.value?.creator?.target) {\n const creatorId = typeof playlist.value.creator.target === 'object' \n ? playlist.value.creator.target._id \n : playlist.value.creator.target;\n \n const playlists = await playlistsActions.fetchPlaylists({\n 'creator.target': creatorId,\n isPublic: true,\n limit: 6\n });\n \n // Filter out current playlist\n morePlaylists.value = playlists.filter(p => p._id !== playlist.value._id).slice(0, 5);\n }\n } catch (error) {\n console.error('Error fetching playlist data:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchPlaylistData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>"],"names":["playlistsState","authState","playlist","playerActions","playlistsActions","globals.actions"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Playlist.vue.js","sources":["../../../../../../../src/modules/music/components/pages/Playlist.vue"],"sourcesContent":["<template>\n <div class=\"playlist-page pd-small\">\n <!-- Not Found -->\n <div v-if=\"hasLoaded && !playlist\" class=\"t-center pd-big\">\n <h2 class=\"\">Playlist not found</h2>\n <p class=\"t-transp t-medium\">The playlist you're looking for doesn't exist or has been removed.</p>\n </div>\n \n <!-- Playlist Content -->\n <div v-if=\"playlist\" class=\"playlist-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative playlist-cover-section\">\n <!-- Cover -->\n <div class=\"cover-container relative mn-b-medium radius-big overflow-hidden shadow-big\">\n <Media \n :url=\"playlist.coverUrl || '/assets/placeholder-playlist.jpg'\"\n :alt=\"playlist.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n </div>\n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ playlistTracks.length }}</div>\n <div class=\"t-small t-transp t-uppercase\">Tracks</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(playlist.followers || 0) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Followers</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Playlist Details -->\n <div class=\"playlist-details-section\">\n <!-- Playlist Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase\">\n Playlist\n </span>\n <span v-if=\"playlist.isCollaborative\" class=\"badge bg-secondary-transp-20 t-secondary pd-thin-big radius-small t-small\">\n Collaborative\n </span>\n <span v-if=\"playlist.status === 'published'\" class=\"badge bg-success-transp-20 t-success pd-thin-big radius-small t-small\">\n Published\n </span>\n </div>\n\n <!-- Playlist Title -->\n <h1 class=\"h1 mn-b-medium\">{{ playlist.title }}</h1>\n\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playPlaylist\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 t-white bg-black radius-thin flex-center gap-thin\"\n >\n <IconPlay fill=\"rgb(var(--white))\" class=\"i-medium\" />\n Play All\n </Button>\n\n <Button\n @click=\"shufflePlay\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconShuffle class=\"i-medium\" />\n Shuffle\n </Button>\n\n <Button\n @click=\"toggleFollow\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n {{isFollowing ? 'Follow' : 'Unfollow'}}\n </Button>\n\n \n <Dropdown :label=\"{component: IconEllipsis, class: 'bg-light radius-thin pd-thin i-big' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-white pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"addToQueue\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Queue\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner || isCollaborator\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editPlaylist\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Playlist\n </Button>\n <Button v-if=\"isOwner\" @click=\"toggleCollaborative\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n {{ playlist.isCollaborative ? 'Make Private' : 'Make Collaborative' }}\n </Button>\n <Button v-if=\"isOwner\" @click=\"deletePlaylist\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Playlist\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Owner/Creator Card -->\n <div class=\"owner-section mn-b-big\">\n <h3 class=\"t-medium mn-b-small\">Created by</h3>\n <div class=\"owner-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <router-link \n :to=\"getOwnerProfileLink(playlist.creator || playlist.owner)\"\n class=\"flex items-center gap-medium flex-1 hover-opacity\"\n >\n <div class=\"owner-avatar\">\n <Media \n v-if=\"getOwnerData(playlist)?.photoUrl\"\n :url=\"getOwnerData(playlist).photoUrl\"\n :alt=\"getOwnerData(playlist)?.name\"\n class=\"w-4r h-4r radius-full object-cover\"\n />\n <div v-else class=\"w-4r h-4r radius-full bg-primary flex-center \">\n {{ getPlaylistOwnerName(playlist).charAt(0) }}\n </div>\n </div>\n <div>\n <div class=\"flex items-center gap-thin\">\n <span class=\"t-large \">{{ getPlaylistOwnerName(playlist) }}</span>\n <IconVerified v-if=\"getOwnerData(playlist)?.isVerified\" class=\"w-1r h-1r t-primary\" />\n </div>\n <span class=\"t-small t-transp\">{{ playlist.creator?.type || 'User' }}</span>\n </div>\n </router-link>\n <Button \n v-if=\"!isOwner && authState.user\"\n @click=\"() => toggleFollowUser(getOwnerId(playlist))\"\n :color=\"followedUsers.includes(getOwnerId(playlist)) ? 'primary' : 'transp'\"\n size=\"small\"\n >\n {{ followedUsers.includes(getOwnerId(playlist)) ? 'Following' : 'Follow' }}\n </Button>\n </div>\n </div>\n\n <!-- Collaborators -->\n <div v-if=\"playlist.collaborators && playlist.collaborators.length > 0\" class=\"collaborators-section mn-b-big\">\n <h3 class=\"t-medium mn-b-small\">Collaborators</h3>\n <div class=\"flex flex-wrap gap-small\">\n <div \n v-for=\"collaborator in playlist.collaborators\"\n :key=\"collaborator._id || collaborator\"\n class=\"collaborator-chip bg-light pd-thin-big radius-full flex items-center gap-thin\"\n >\n <Media \n v-if=\"collaborator.photoUrl\"\n :url=\"collaborator.photoUrl\"\n class=\"i-regular radius-full object-cover\"\n />\n <span class=\"t-small\">{{ collaborator.name || collaborator.profile?.name || 'User' }}</span>\n </div>\n </div>\n </div>\n\n <!-- Metadata Cards -->\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-big\">\n <!-- Created Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCalendar class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Created</div>\n <div class=\"t-medium \">{{ formatDate(playlist.createdAt) }}</div>\n </div>\n </div>\n\n <!-- Total Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconClock class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ totalDuration }}</div>\n </div>\n </div>\n\n <!-- Updated Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconRefresh class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Updated</div>\n <div class=\"t-medium \">{{ formatDate(playlist.updatedAt) }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconEye class=\"i-medium t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ playlist.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n <!-- Tags -->\n <div v-if=\"playlist.tags && playlist.tags.length\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"tag in playlist.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"playlist.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ playlist.description }}</p>\n </div>\n </div>\n </div>\n\n <!-- Playlist Tracks -->\n <section v-if=\"!isLoading && playlist && playlistTracks.length\" class=\"tracks-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Tracklist</h2>\n <Feed\n :store=\"{\n read: () => Promise.resolve(playlistTracks),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"playlistTracks\"\n :states=\"{\n empty: {\n title: 'No tracks in playlist',\n description: 'Add some tracks to get started',\n class: 'pd-medium t-center'\n }\n }\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in items\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index + 1\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n :canRemove=\"isOwner || isCollaborator\"\n @remove=\"() => removeTrack(track._id)\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- Empty State -->\n <section v-else-if=\"!isLoading && playlist && !playlistTracks.length\" class=\"empty-section mn-t-big\">\n <div class=\"empty-tracks t-center pd-big bg-light radius-medium\">\n <h3 class=\" mn-b-small\">This playlist is empty</h3>\n <p class=\"t-transp t-medium mn-b-medium\">Add some tracks to get started</p>\n \n <Button \n v-if=\"isOwner || isCollaborator\"\n @click=\"$router.push({ name: 'music-search' })\"\n color=\"primary\"\n size=\"medium\"\n >\n Find Tracks\n </Button>\n </div>\n </section>\n\n <!-- More Playlists -->\n <section v-if=\"!isLoading && playlist && morePlaylists.length\" class=\"more-playlists-section mn-t-big\">\n <div class=\"flex justify-between items-center mn-b-medium\">\n <h2 class=\"h2\">More Playlists</h2>\n <router-link \n v-if=\"playlist.creator\"\n :to=\"getOwnerProfileLink(playlist.creator)\" \n class=\"t-primary hover-opacity\"\n >\n See all\n </router-link>\n </div>\n <div class=\"flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide\">\n <li v-for=\"relatedPlaylist in morePlaylists\" :key=\"relatedPlaylist._id\" class=\"flex-none scroll-snap-align-start\">\n <PlaylistCard :playlist=\"relatedPlaylist\" class=\"w-min-15r transition-cubic-in-out\" />\n </li>\n </div>\n </section>\n\n <!-- Edit Playlist Modal -->\n <Popup \n :isPopupOpen=\"showEditModal && (isOwner || isCollaborator)\"\n @close-popup=\"showEditModal = false\" \n class=\"bg-white pd-medium w-m-30r radius-medium\"\n >\n <PlaylistForm \n :editMode=\"true\"\n :url=\"playlist.url\"\n @cancel=\"showEditModal = false\"\n @updated=\"handlePlaylistUpdated\"\n />\n </Popup>\n\n <!-- Delete Confirmation Modal -->\n <Popup \n :isPopupOpen=\"showDeleteModal\"\n @close-popup=\"showDeleteModal = false\" \n class=\"bg-white pd-medium w-m-25r radius-medium\"\n >\n <h3 class=\"mn-b-medium\">Delete Playlist</h3>\n <p class=\"t-transp mn-b-medium\">Are you sure you want to delete \"{{ playlist.title }}\"? This action cannot be undone.</p>\n \n <div class=\"flex justify-end gap-small\">\n <Button \n @click=\"showDeleteModal = false\"\n color=\"transp\"\n size=\"medium\"\n >\n Cancel\n </Button>\n \n <Button \n @click=\"confirmDelete\"\n color=\"danger\"\n size=\"medium\"\n >\n Delete Playlist\n </Button>\n </div>\n </Popup>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\nimport Popup from '@martyrs/src/components/Popup/Popup.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconRefresh from '@martyrs/src/modules/icons/navigation/IconRefresh.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport PlaylistCard from '../cards/PlaylistCard.vue';\nimport PlaylistForm from '../forms/PlaylistForm.vue';\n\n// Store\nimport { state as playlistsState, actions as playlistsActions } from '../../store/playlists.js';\nimport { state as tracksState, actions as tracksActions } from '../../store/tracks.js';\nimport { actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\nimport * as globals from '@martyrs/src/modules/globals/views/store/globals.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst isFollowing = ref(false);\nconst showDropdown = ref(false);\nconst showEditModal = ref(false);\nconst showDeleteModal = ref(false);\nconst followedUsers = ref([]);\nconst morePlaylists = ref([]);\n\n// Clear state\nplaylistsState.currentPlaylist = null;\nplaylistsState.currentPlaylistTracks = [];\n\n// Computed\nconst playlist = computed(() => playlistsState.currentPlaylist);\nconst playlistTracks = computed(() => playlistsState.currentPlaylistTracks || []);\n\nconst isOwner = computed(() => {\n if (!playlist.value || !authState.user) return false;\n \n const ownerId = playlist.value.owner?.target?._id || playlist.value.owner?.target;\n return ownerId === authState.user._id;\n});\n\nconst isCollaborator = computed(() => {\n if (!playlist.value || !authState.user) return false;\n \n return playlist.value.collaborators?.some(collab => \n (collab._id || collab) === authState.user._id\n );\n});\n\nconst totalDuration = computed(() => {\n if (!playlistTracks.value.length) return '0:00';\n const totalSeconds = playlistTracks.value.reduce((sum, track) => sum + (track.duration || 0), 0);\n return formatDuration(totalSeconds);\n});\n\n// Helper functions\nconst getOwnerData = (playlist) => {\n if (!playlist) return null;\n const owner = playlist.creator?.target || playlist.owner?.target;\n return typeof owner === 'object' ? owner : null;\n};\n\nconst getOwnerId = (playlist) => {\n if (!playlist) return null;\n const owner = playlist.creator?.target || playlist.owner?.target;\n return typeof owner === 'object' ? owner._id : owner;\n};\n\nconst getPlaylistOwnerName = (playlist) => {\n if (!playlist) return 'Unknown';\n \n const owner = getOwnerData(playlist);\n if (owner) {\n return owner.profile?.name || owner.name || 'Unknown';\n }\n \n return 'Unknown';\n};\n\nconst getOwnerProfileLink = (owner) => {\n if (!owner || !owner.target) return { name: 'music-home' };\n \n const targetId = typeof owner.target === 'object' ? owner.target._id : owner.target;\n \n if (owner.type === 'user' || owner.type === 'User') {\n return { name: 'User Profile', params: { _id: targetId } };\n } else if (owner.type === 'organization' || owner.type === 'Organization') {\n return { name: 'Organizatio', params: { _id: targetId } };\n }\n \n return { name: 'music-home' };\n};\n\n// Format helpers\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playPlaylist = () => {\n if (playlistTracks.value && playlistTracks.value.length > 0) {\n playerActions.setQueue(playlistTracks.value);\n }\n};\n\nconst shufflePlay = () => {\n if (playlistTracks.value && playlistTracks.value.length > 0) {\n const shuffled = [...playlistTracks.value].sort(() => Math.random() - 0.5);\n playerActions.setQueue(shuffled);\n }\n};\n\nconst toggleFollow = async () => {\n isFollowing.value = !isFollowing.value;\n // TODO: Implement actual following\n try {\n if (isFollowing.value) {\n await playlistsActions.followPlaylist(playlist.value._id);\n } else {\n await playlistsActions.unfollowPlaylist(playlist.value._id);\n }\n } catch (error) {\n console.error('Error toggling follow:', error);\n isFollowing.value = !isFollowing.value; // Revert on error\n }\n};\n\nconst toggleFollowUser = (userId) => {\n const index = followedUsers.value.indexOf(userId);\n if (index > -1) {\n followedUsers.value.splice(index, 1);\n } else {\n followedUsers.value.push(userId);\n }\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (playlistTracks.value && playlistTracks.value.length > 0) {\n playlistTracks.value.forEach(track => {\n playerActions.addToQueue(track);\n });\n showDropdown.value = false;\n }\n};\n\nconst editPlaylist = () => {\n showEditModal.value = true;\n showDropdown.value = false;\n};\n\nconst toggleCollaborative = async () => {\n try {\n const updatedData = {\n _id: playlist.value._id,\n isCollaborative: !playlist.value.isCollaborative\n };\n \n await playlistsActions.updatePlaylist(updatedData);\n showDropdown.value = false;\n } catch (error) {\n console.error('Error updating playlist:', error);\n }\n};\n\nconst deletePlaylist = () => {\n showDeleteModal.value = true;\n showDropdown.value = false;\n};\n\nconst confirmDelete = async () => {\n try {\n await playlistsActions.deletePlaylist(playlist.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Error deleting playlist:', error);\n globals.actions.setError({\n message: 'Failed to delete playlist'\n });\n }\n};\n\nconst removeTrack = async (trackId) => {\n try {\n await playlistsActions.removeTrackFromPlaylist(playlist.value._id, trackId);\n // Refresh playlist data\n await fetchPlaylistData();\n } catch (error) {\n console.error('Error removing track:', error);\n globals.actions.setError({\n message: 'Failed to remove track'\n });\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\nconst handlePlaylistUpdated = () => {\n showEditModal.value = false;\n fetchPlaylistData();\n};\n\n// Data fetching\nconst fetchPlaylistData = async () => {\n try {\n await playlistsActions.fetchPlaylistByUrl(route.params.url);\n \n // Check if following\n if (authState.user && playlist.value) {\n // TODO: Check if user is following this playlist\n }\n \n // Fetch more playlists from the same creator\n if (playlist.value?.creator?.target) {\n const creatorId = typeof playlist.value.creator.target === 'object' \n ? playlist.value.creator.target._id \n : playlist.value.creator.target;\n \n const playlists = await playlistsActions.fetchPlaylists({\n 'creator.target': creatorId,\n isPublic: true,\n limit: 6\n });\n \n // Filter out current playlist\n morePlaylists.value = playlists.filter(p => p._id !== playlist.value._id).slice(0, 5);\n }\n } catch (error) {\n console.error('Error fetching playlist data:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchPlaylistData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>"],"names":["playlistsState","authState","playlist","playerActions","playlistsActions","globals.actions"],"mappings":"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"}
|
|
@@ -161,7 +161,7 @@ const _sfc_main = {
|
|
|
161
161
|
key: filter.id,
|
|
162
162
|
onClick: ($event) => setActiveFilter(filter.id),
|
|
163
163
|
class: vue.normalizeClass([[
|
|
164
|
-
filter.id === activeFilter.value ? "bg-white t-black" : "bg-
|
|
164
|
+
filter.id === activeFilter.value ? "bg-white t-black" : "bg-white-transp-50 hover-bg-white"
|
|
165
165
|
], "radius-extra pd-small"]),
|
|
166
166
|
showLoader: false,
|
|
167
167
|
showSucces: false
|
|
@@ -191,7 +191,7 @@ const _sfc_main = {
|
|
|
191
191
|
trackResults.value.length > 5 && activeFilter.value === "all" ? (vue.openBlock(), vue.createBlock(Button.default, {
|
|
192
192
|
key: 0,
|
|
193
193
|
onClick: _cache[0] || (_cache[0] = ($event) => setActiveFilter("tracks")),
|
|
194
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
194
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
195
195
|
showLoader: false,
|
|
196
196
|
showSucces: false
|
|
197
197
|
}, {
|
|
@@ -219,7 +219,7 @@ const _sfc_main = {
|
|
|
219
219
|
artistResults.value.length > 6 && activeFilter.value === "all" ? (vue.openBlock(), vue.createBlock(Button.default, {
|
|
220
220
|
key: 0,
|
|
221
221
|
onClick: _cache[1] || (_cache[1] = ($event) => setActiveFilter("artists")),
|
|
222
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
222
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
223
223
|
showLoader: false,
|
|
224
224
|
showSucces: false
|
|
225
225
|
}, {
|
|
@@ -245,7 +245,7 @@ const _sfc_main = {
|
|
|
245
245
|
albumResults.value.length > 5 && activeFilter.value === "all" ? (vue.openBlock(), vue.createBlock(Button.default, {
|
|
246
246
|
key: 0,
|
|
247
247
|
onClick: _cache[2] || (_cache[2] = ($event) => setActiveFilter("albums")),
|
|
248
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
248
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
249
249
|
showLoader: false,
|
|
250
250
|
showSucces: false
|
|
251
251
|
}, {
|
|
@@ -271,7 +271,7 @@ const _sfc_main = {
|
|
|
271
271
|
playlistResults.value.length > 5 && activeFilter.value === "all" ? (vue.openBlock(), vue.createBlock(Button.default, {
|
|
272
272
|
key: 0,
|
|
273
273
|
onClick: _cache[3] || (_cache[3] = ($event) => setActiveFilter("playlists")),
|
|
274
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
274
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
275
275
|
showLoader: false,
|
|
276
276
|
showSucces: false
|
|
277
277
|
}, {
|
|
@@ -297,7 +297,7 @@ const _sfc_main = {
|
|
|
297
297
|
genreResults.value.length > 4 && activeFilter.value === "all" ? (vue.openBlock(), vue.createBlock(Button.default, {
|
|
298
298
|
key: 0,
|
|
299
299
|
onClick: _cache[4] || (_cache[4] = ($event) => setActiveFilter("genres")),
|
|
300
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
300
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
301
301
|
showLoader: false,
|
|
302
302
|
showSucces: false
|
|
303
303
|
}, {
|
|
@@ -330,6 +330,6 @@ const _sfc_main = {
|
|
|
330
330
|
};
|
|
331
331
|
}
|
|
332
332
|
};
|
|
333
|
-
const SearchResults = /* @__PURE__ */ _pluginVue_exportHelper.default(_sfc_main, [["__scopeId", "data-v-
|
|
333
|
+
const SearchResults = /* @__PURE__ */ _pluginVue_exportHelper.default(_sfc_main, [["__scopeId", "data-v-e47a0d68"]]);
|
|
334
334
|
exports.default = SearchResults;
|
|
335
335
|
//# sourceMappingURL=SearchResults.vue.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchResults.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/SearchResults.vue"],"sourcesContent":["<!-- components/pages/SearchResults.vue -->\n<template>\n <div class=\"search-results-page pd-medium\">\n <div class=\"search-header mn-b-medium\">\n <h1 class=\"mn-b-small\">Search</h1>\n \n <BlockSearch \n :placeholder=\"'What do you want to listen to?'\"\n class=\"bg-light w-m-40r\"\n @search=\"handleSearch\"\n :autofocus=\"true\"\n />\n \n <div v-if=\"searchQuery\" class=\"search-filters flex gap-small mn-t-medium\">\n <Button \n v-for=\"filter in searchFilters\"\n :key=\"filter.id\"\n @click=\"setActiveFilter(filter.id)\"\n :class=\"[\n filter.id === activeFilter ? 'bg-white t-black' : 'bg-dark-transp-50 hover-bg-dark',\n ]\"\n class=\"radius-extra pd-small\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n {{ filter.label }}\n </Button>\n </div>\n </div>\n \n <div v-if=\"isLoading\" class=\"search-loading\">\n <Loader />\n </div>\n \n <div v-else-if=\"searchError\" class=\"search-error t-center pd-big\">\n <p class=\"t-fourth t-medium\">{{ searchError }}</p>\n </div>\n \n <div v-else-if=\"!searchQuery\" class=\"search-empty t-center pd-big\">\n <h2 class=\"mn-b-small\">Search for music</h2>\n <p class=\"t-transp t-medium\">Find your favorite songs, artists, albums, and playlists</p>\n </div>\n \n <div v-else-if=\"!hasResults\" class=\"search-no-results t-center pd-big\">\n <h2 class=\"mn-b-small\">No results found for \"{{ searchQuery }}\"</h2>\n <p class=\"t-transp t-medium\">Please try different keywords or check your spelling</p>\n </div>\n \n <div v-else class=\"search-results\">\n <!-- Songs Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'tracks') && trackResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Songs</h2>\n <Button \n v-if=\"trackResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('tracks')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in (activeFilter === 'all' ? trackResults.slice(0, 5) : trackResults)\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </section>\n \n <!-- Artists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'artists') && artistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Artists</h2>\n <Button \n v-if=\"artistResults.length > 6 && activeFilter === 'all'\"\n @click=\"setActiveFilter('artists')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"artists-grid cols-6 mobile:cols-3 gap-small\">\n <div v-for=\"artist in (activeFilter === 'all' ? artistResults.slice(0, 6) : artistResults)\" :key=\"artist._id\">\n <ArtistCard :artist=\"artist\" />\n </div>\n </div>\n </section>\n \n <!-- Albums Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'albums') && albumResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Albums</h2>\n <Button \n v-if=\"albumResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('albums')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"albums-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"album in (activeFilter === 'all' ? albumResults.slice(0, 5) : albumResults)\" :key=\"album._id\">\n <AlbumCard :album=\"album\" />\n </div>\n </div>\n </section>\n \n <!-- Playlists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'playlists') && playlistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Playlists</h2>\n <Button \n v-if=\"playlistResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('playlists')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"playlists-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"playlist in (activeFilter === 'all' ? playlistResults.slice(0, 5) : playlistResults)\" :key=\"playlist._id\">\n <PlaylistCard :playlist=\"playlist\" />\n </div>\n </div>\n </section>\n \n <!-- Genres Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'genres') && genreResults.length > 0\" class=\"search-section\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Genres</h2>\n <Button \n v-if=\"genreResults.length > 4 && activeFilter === 'all'\"\n @click=\"setActiveFilter('genres')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"genres-grid cols-4 mobile:cols-2 gap-small\">\n <router-link \n v-for=\"genre in (activeFilter === 'all' ? genreResults.slice(0, 4) : genreResults)\" \n :key=\"genre._id\"\n :to=\"{ name: 'genre-detail', params: { url: genre.url } }\"\n class=\"genre-card bg-gradient-color pd-medium radius-medium t-center hover-scale-1 transition-cubic-in-out\"\n :style=\"{ \n '--gradient-color': getRandomGradient() \n }\"\n >\n <h3 class=\"\">{{ genre.name }}</h3>\n </router-link>\n </div>\n </section>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport BlockSearch from '@martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue';\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport AlbumCard from '../cards/AlbumCard.vue';\nimport PlaylistCard from '../cards/PlaylistCard.vue';\nimport ArtistCard from '../cards/ArtistCard.vue';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\n\n// Import search store\nimport { state as searchState, actions as searchActions } from '../../store/search.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// State\nconst searchQuery = ref('');\nconst activeFilter = ref('all');\n\n// Array of search filters\nconst searchFilters = [\n { id: 'all', label: 'All' },\n { id: 'tracks', label: 'Songs' },\n { id: 'artists', label: 'Artists' },\n { id: 'albums', label: 'Albums' },\n { id: 'playlists', label: 'Playlists' },\n { id: 'genres', label: 'Genres' }\n];\n\n// Computed properties\nconst isLoading = computed(() => searchState.isLoading);\nconst searchError = computed(() => searchState.error);\nconst hasResults = computed(() => {\n return trackResults.value.length > 0 || \n artistResults.value.length > 0 || \n albumResults.value.length > 0 || \n playlistResults.value.length > 0 || \n genreResults.value.length > 0;\n});\n\nconst trackResults = computed(() => searchState.results.tracks || []);\nconst artistResults = computed(() => searchState.results.artists || []);\nconst albumResults = computed(() => searchState.results.albums || []);\nconst playlistResults = computed(() => searchState.results.playlists || []);\nconst genreResults = computed(() => searchState.results.genres || []);\n\n// Methods\nconst handleSearch = (query) => {\n searchQuery.value = query;\n \n if (query.trim()) {\n // Update URL without reloading the page\n router.push({ \n name: 'music-search', \n query: { q: query },\n replace: true\n });\n \n // Perform search\n searchActions.search(query);\n } else {\n // Clear search when query is empty\n router.push({ \n name: 'music-search',\n replace: true\n });\n searchActions.clearSearch();\n }\n};\n\nconst setActiveFilter = (filter) => {\n activeFilter.value = filter;\n searchActions.setFilter(filter);\n};\n\n// Generate random gradient for genre cards\nconst getRandomGradient = () => {\n const colors = [\n 'linear-gradient(135deg, #1DB954, #1ED760)',\n 'linear-gradient(135deg, #FF6B6B, #FFE66D)',\n 'linear-gradient(135deg, #4776E6, #8E54E9)',\n 'linear-gradient(135deg, #FF8008, #FFC837)',\n 'linear-gradient(135deg, #7F00FF, #E100FF)',\n 'linear-gradient(135deg, #11998E, #38EF7D)'\n ];\n \n return colors[Math.floor(Math.random() * colors.length)];\n};\n\n// Watch for URL query parameter changes\nonMounted(() => {\n const query = route.query.q;\n if (query) {\n searchQuery.value = query;\n searchActions.search(query);\n }\n});\n\nwatch(() => route.query.q, (newQuery) => {\n if (newQuery && newQuery !== searchQuery.value) {\n searchQuery.value = newQuery;\n searchActions.search(newQuery);\n } else if (!newQuery) {\n searchQuery.value = '';\n searchActions.clearSearch();\n }\n});\n</script>\n\n<style scoped>\n.bg-gradient-color {\n background: var(--gradient-color, linear-gradient(135deg, #1DB954, #1ED760));\n}\n</style>"],"names":["useRoute","useRouter","ref","computed","searchState","searchActions","onMounted","watch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SearchResults.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/SearchResults.vue"],"sourcesContent":["<!-- components/pages/SearchResults.vue -->\n<template>\n <div class=\"search-results-page pd-medium\">\n <div class=\"search-header mn-b-medium\">\n <h1 class=\"mn-b-small\">Search</h1>\n \n <BlockSearch \n :placeholder=\"'What do you want to listen to?'\"\n class=\"bg-light w-m-40r\"\n @search=\"handleSearch\"\n :autofocus=\"true\"\n />\n \n <div v-if=\"searchQuery\" class=\"search-filters flex gap-small mn-t-medium\">\n <Button \n v-for=\"filter in searchFilters\"\n :key=\"filter.id\"\n @click=\"setActiveFilter(filter.id)\"\n :class=\"[\n filter.id === activeFilter ? 'bg-white t-black' : 'bg-white-transp-50 hover-bg-white',\n ]\"\n class=\"radius-extra pd-small\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n {{ filter.label }}\n </Button>\n </div>\n </div>\n \n <div v-if=\"isLoading\" class=\"search-loading\">\n <Loader />\n </div>\n \n <div v-else-if=\"searchError\" class=\"search-error t-center pd-big\">\n <p class=\"t-fourth t-medium\">{{ searchError }}</p>\n </div>\n \n <div v-else-if=\"!searchQuery\" class=\"search-empty t-center pd-big\">\n <h2 class=\"mn-b-small\">Search for music</h2>\n <p class=\"t-transp t-medium\">Find your favorite songs, artists, albums, and playlists</p>\n </div>\n \n <div v-else-if=\"!hasResults\" class=\"search-no-results t-center pd-big\">\n <h2 class=\"mn-b-small\">No results found for \"{{ searchQuery }}\"</h2>\n <p class=\"t-transp t-medium\">Please try different keywords or check your spelling</p>\n </div>\n \n <div v-else class=\"search-results\">\n <!-- Songs Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'tracks') && trackResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Songs</h2>\n <Button \n v-if=\"trackResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('tracks')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in (activeFilter === 'all' ? trackResults.slice(0, 5) : trackResults)\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </section>\n \n <!-- Artists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'artists') && artistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Artists</h2>\n <Button \n v-if=\"artistResults.length > 6 && activeFilter === 'all'\"\n @click=\"setActiveFilter('artists')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"artists-grid cols-6 mobile:cols-3 gap-small\">\n <div v-for=\"artist in (activeFilter === 'all' ? artistResults.slice(0, 6) : artistResults)\" :key=\"artist._id\">\n <ArtistCard :artist=\"artist\" />\n </div>\n </div>\n </section>\n \n <!-- Albums Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'albums') && albumResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Albums</h2>\n <Button \n v-if=\"albumResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('albums')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"albums-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"album in (activeFilter === 'all' ? albumResults.slice(0, 5) : albumResults)\" :key=\"album._id\">\n <AlbumCard :album=\"album\" />\n </div>\n </div>\n </section>\n \n <!-- Playlists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'playlists') && playlistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Playlists</h2>\n <Button \n v-if=\"playlistResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('playlists')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"playlists-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"playlist in (activeFilter === 'all' ? playlistResults.slice(0, 5) : playlistResults)\" :key=\"playlist._id\">\n <PlaylistCard :playlist=\"playlist\" />\n </div>\n </div>\n </section>\n \n <!-- Genres Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'genres') && genreResults.length > 0\" class=\"search-section\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Genres</h2>\n <Button \n v-if=\"genreResults.length > 4 && activeFilter === 'all'\"\n @click=\"setActiveFilter('genres')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"genres-grid cols-4 mobile:cols-2 gap-small\">\n <router-link \n v-for=\"genre in (activeFilter === 'all' ? genreResults.slice(0, 4) : genreResults)\" \n :key=\"genre._id\"\n :to=\"{ name: 'genre-detail', params: { url: genre.url } }\"\n class=\"genre-card bg-gradient-color pd-medium radius-medium t-center hover-scale-1 transition-cubic-in-out\"\n :style=\"{ \n '--gradient-color': getRandomGradient() \n }\"\n >\n <h3 class=\"\">{{ genre.name }}</h3>\n </router-link>\n </div>\n </section>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport BlockSearch from '@martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue';\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport AlbumCard from '../cards/AlbumCard.vue';\nimport PlaylistCard from '../cards/PlaylistCard.vue';\nimport ArtistCard from '../cards/ArtistCard.vue';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\n\n// Import search store\nimport { state as searchState, actions as searchActions } from '../../store/search.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// State\nconst searchQuery = ref('');\nconst activeFilter = ref('all');\n\n// Array of search filters\nconst searchFilters = [\n { id: 'all', label: 'All' },\n { id: 'tracks', label: 'Songs' },\n { id: 'artists', label: 'Artists' },\n { id: 'albums', label: 'Albums' },\n { id: 'playlists', label: 'Playlists' },\n { id: 'genres', label: 'Genres' }\n];\n\n// Computed properties\nconst isLoading = computed(() => searchState.isLoading);\nconst searchError = computed(() => searchState.error);\nconst hasResults = computed(() => {\n return trackResults.value.length > 0 || \n artistResults.value.length > 0 || \n albumResults.value.length > 0 || \n playlistResults.value.length > 0 || \n genreResults.value.length > 0;\n});\n\nconst trackResults = computed(() => searchState.results.tracks || []);\nconst artistResults = computed(() => searchState.results.artists || []);\nconst albumResults = computed(() => searchState.results.albums || []);\nconst playlistResults = computed(() => searchState.results.playlists || []);\nconst genreResults = computed(() => searchState.results.genres || []);\n\n// Methods\nconst handleSearch = (query) => {\n searchQuery.value = query;\n \n if (query.trim()) {\n // Update URL without reloading the page\n router.push({ \n name: 'music-search', \n query: { q: query },\n replace: true\n });\n \n // Perform search\n searchActions.search(query);\n } else {\n // Clear search when query is empty\n router.push({ \n name: 'music-search',\n replace: true\n });\n searchActions.clearSearch();\n }\n};\n\nconst setActiveFilter = (filter) => {\n activeFilter.value = filter;\n searchActions.setFilter(filter);\n};\n\n// Generate random gradient for genre cards\nconst getRandomGradient = () => {\n const colors = [\n 'linear-gradient(135deg, #1DB954, #1ED760)',\n 'linear-gradient(135deg, #FF6B6B, #FFE66D)',\n 'linear-gradient(135deg, #4776E6, #8E54E9)',\n 'linear-gradient(135deg, #FF8008, #FFC837)',\n 'linear-gradient(135deg, #7F00FF, #E100FF)',\n 'linear-gradient(135deg, #11998E, #38EF7D)'\n ];\n \n return colors[Math.floor(Math.random() * colors.length)];\n};\n\n// Watch for URL query parameter changes\nonMounted(() => {\n const query = route.query.q;\n if (query) {\n searchQuery.value = query;\n searchActions.search(query);\n }\n});\n\nwatch(() => route.query.q, (newQuery) => {\n if (newQuery && newQuery !== searchQuery.value) {\n searchQuery.value = newQuery;\n searchActions.search(newQuery);\n } else if (!newQuery) {\n searchQuery.value = '';\n searchActions.clearSearch();\n }\n});\n</script>\n\n<style scoped>\n.bg-gradient-color {\n background: var(--gradient-color, linear-gradient(135deg, #1DB954, #1ED760));\n}\n</style>"],"names":["useRoute","useRouter","ref","computed","searchState","searchActions","onMounted","watch"],"mappings":"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"}
|
|
@@ -159,7 +159,7 @@ const _sfc_main = {
|
|
|
159
159
|
key: filter.id,
|
|
160
160
|
onClick: ($event) => setActiveFilter(filter.id),
|
|
161
161
|
class: normalizeClass([[
|
|
162
|
-
filter.id === activeFilter.value ? "bg-white t-black" : "bg-
|
|
162
|
+
filter.id === activeFilter.value ? "bg-white t-black" : "bg-white-transp-50 hover-bg-white"
|
|
163
163
|
], "radius-extra pd-small"]),
|
|
164
164
|
showLoader: false,
|
|
165
165
|
showSucces: false
|
|
@@ -189,7 +189,7 @@ const _sfc_main = {
|
|
|
189
189
|
trackResults.value.length > 5 && activeFilter.value === "all" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
190
190
|
key: 0,
|
|
191
191
|
onClick: _cache[0] || (_cache[0] = ($event) => setActiveFilter("tracks")),
|
|
192
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
192
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
193
193
|
showLoader: false,
|
|
194
194
|
showSucces: false
|
|
195
195
|
}, {
|
|
@@ -217,7 +217,7 @@ const _sfc_main = {
|
|
|
217
217
|
artistResults.value.length > 6 && activeFilter.value === "all" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
218
218
|
key: 0,
|
|
219
219
|
onClick: _cache[1] || (_cache[1] = ($event) => setActiveFilter("artists")),
|
|
220
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
220
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
221
221
|
showLoader: false,
|
|
222
222
|
showSucces: false
|
|
223
223
|
}, {
|
|
@@ -243,7 +243,7 @@ const _sfc_main = {
|
|
|
243
243
|
albumResults.value.length > 5 && activeFilter.value === "all" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
244
244
|
key: 0,
|
|
245
245
|
onClick: _cache[2] || (_cache[2] = ($event) => setActiveFilter("albums")),
|
|
246
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
246
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
247
247
|
showLoader: false,
|
|
248
248
|
showSucces: false
|
|
249
249
|
}, {
|
|
@@ -269,7 +269,7 @@ const _sfc_main = {
|
|
|
269
269
|
playlistResults.value.length > 5 && activeFilter.value === "all" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
270
270
|
key: 0,
|
|
271
271
|
onClick: _cache[3] || (_cache[3] = ($event) => setActiveFilter("playlists")),
|
|
272
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
272
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
273
273
|
showLoader: false,
|
|
274
274
|
showSucces: false
|
|
275
275
|
}, {
|
|
@@ -295,7 +295,7 @@ const _sfc_main = {
|
|
|
295
295
|
genreResults.value.length > 4 && activeFilter.value === "all" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
296
296
|
key: 0,
|
|
297
297
|
onClick: _cache[4] || (_cache[4] = ($event) => setActiveFilter("genres")),
|
|
298
|
-
class: "t-main bg-transparent border-none hover-bg-
|
|
298
|
+
class: "t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin",
|
|
299
299
|
showLoader: false,
|
|
300
300
|
showSucces: false
|
|
301
301
|
}, {
|
|
@@ -328,7 +328,7 @@ const _sfc_main = {
|
|
|
328
328
|
};
|
|
329
329
|
}
|
|
330
330
|
};
|
|
331
|
-
const SearchResults = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
331
|
+
const SearchResults = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-e47a0d68"]]);
|
|
332
332
|
export {
|
|
333
333
|
SearchResults as default
|
|
334
334
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchResults.vue.js","sources":["../../../../../../../src/modules/music/components/pages/SearchResults.vue"],"sourcesContent":["<!-- components/pages/SearchResults.vue -->\n<template>\n <div class=\"search-results-page pd-medium\">\n <div class=\"search-header mn-b-medium\">\n <h1 class=\"mn-b-small\">Search</h1>\n \n <BlockSearch \n :placeholder=\"'What do you want to listen to?'\"\n class=\"bg-light w-m-40r\"\n @search=\"handleSearch\"\n :autofocus=\"true\"\n />\n \n <div v-if=\"searchQuery\" class=\"search-filters flex gap-small mn-t-medium\">\n <Button \n v-for=\"filter in searchFilters\"\n :key=\"filter.id\"\n @click=\"setActiveFilter(filter.id)\"\n :class=\"[\n filter.id === activeFilter ? 'bg-white t-black' : 'bg-dark-transp-50 hover-bg-dark',\n ]\"\n class=\"radius-extra pd-small\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n {{ filter.label }}\n </Button>\n </div>\n </div>\n \n <div v-if=\"isLoading\" class=\"search-loading\">\n <Loader />\n </div>\n \n <div v-else-if=\"searchError\" class=\"search-error t-center pd-big\">\n <p class=\"t-fourth t-medium\">{{ searchError }}</p>\n </div>\n \n <div v-else-if=\"!searchQuery\" class=\"search-empty t-center pd-big\">\n <h2 class=\"mn-b-small\">Search for music</h2>\n <p class=\"t-transp t-medium\">Find your favorite songs, artists, albums, and playlists</p>\n </div>\n \n <div v-else-if=\"!hasResults\" class=\"search-no-results t-center pd-big\">\n <h2 class=\"mn-b-small\">No results found for \"{{ searchQuery }}\"</h2>\n <p class=\"t-transp t-medium\">Please try different keywords or check your spelling</p>\n </div>\n \n <div v-else class=\"search-results\">\n <!-- Songs Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'tracks') && trackResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Songs</h2>\n <Button \n v-if=\"trackResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('tracks')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in (activeFilter === 'all' ? trackResults.slice(0, 5) : trackResults)\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </section>\n \n <!-- Artists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'artists') && artistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Artists</h2>\n <Button \n v-if=\"artistResults.length > 6 && activeFilter === 'all'\"\n @click=\"setActiveFilter('artists')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"artists-grid cols-6 mobile:cols-3 gap-small\">\n <div v-for=\"artist in (activeFilter === 'all' ? artistResults.slice(0, 6) : artistResults)\" :key=\"artist._id\">\n <ArtistCard :artist=\"artist\" />\n </div>\n </div>\n </section>\n \n <!-- Albums Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'albums') && albumResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Albums</h2>\n <Button \n v-if=\"albumResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('albums')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"albums-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"album in (activeFilter === 'all' ? albumResults.slice(0, 5) : albumResults)\" :key=\"album._id\">\n <AlbumCard :album=\"album\" />\n </div>\n </div>\n </section>\n \n <!-- Playlists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'playlists') && playlistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Playlists</h2>\n <Button \n v-if=\"playlistResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('playlists')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"playlists-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"playlist in (activeFilter === 'all' ? playlistResults.slice(0, 5) : playlistResults)\" :key=\"playlist._id\">\n <PlaylistCard :playlist=\"playlist\" />\n </div>\n </div>\n </section>\n \n <!-- Genres Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'genres') && genreResults.length > 0\" class=\"search-section\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Genres</h2>\n <Button \n v-if=\"genreResults.length > 4 && activeFilter === 'all'\"\n @click=\"setActiveFilter('genres')\"\n class=\"t-main bg-transparent border-none hover-bg-dark-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"genres-grid cols-4 mobile:cols-2 gap-small\">\n <router-link \n v-for=\"genre in (activeFilter === 'all' ? genreResults.slice(0, 4) : genreResults)\" \n :key=\"genre._id\"\n :to=\"{ name: 'genre-detail', params: { url: genre.url } }\"\n class=\"genre-card bg-gradient-color pd-medium radius-medium t-center hover-scale-1 transition-cubic-in-out\"\n :style=\"{ \n '--gradient-color': getRandomGradient() \n }\"\n >\n <h3 class=\"\">{{ genre.name }}</h3>\n </router-link>\n </div>\n </section>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport BlockSearch from '@martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue';\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport AlbumCard from '../cards/AlbumCard.vue';\nimport PlaylistCard from '../cards/PlaylistCard.vue';\nimport ArtistCard from '../cards/ArtistCard.vue';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\n\n// Import search store\nimport { state as searchState, actions as searchActions } from '../../store/search.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// State\nconst searchQuery = ref('');\nconst activeFilter = ref('all');\n\n// Array of search filters\nconst searchFilters = [\n { id: 'all', label: 'All' },\n { id: 'tracks', label: 'Songs' },\n { id: 'artists', label: 'Artists' },\n { id: 'albums', label: 'Albums' },\n { id: 'playlists', label: 'Playlists' },\n { id: 'genres', label: 'Genres' }\n];\n\n// Computed properties\nconst isLoading = computed(() => searchState.isLoading);\nconst searchError = computed(() => searchState.error);\nconst hasResults = computed(() => {\n return trackResults.value.length > 0 || \n artistResults.value.length > 0 || \n albumResults.value.length > 0 || \n playlistResults.value.length > 0 || \n genreResults.value.length > 0;\n});\n\nconst trackResults = computed(() => searchState.results.tracks || []);\nconst artistResults = computed(() => searchState.results.artists || []);\nconst albumResults = computed(() => searchState.results.albums || []);\nconst playlistResults = computed(() => searchState.results.playlists || []);\nconst genreResults = computed(() => searchState.results.genres || []);\n\n// Methods\nconst handleSearch = (query) => {\n searchQuery.value = query;\n \n if (query.trim()) {\n // Update URL without reloading the page\n router.push({ \n name: 'music-search', \n query: { q: query },\n replace: true\n });\n \n // Perform search\n searchActions.search(query);\n } else {\n // Clear search when query is empty\n router.push({ \n name: 'music-search',\n replace: true\n });\n searchActions.clearSearch();\n }\n};\n\nconst setActiveFilter = (filter) => {\n activeFilter.value = filter;\n searchActions.setFilter(filter);\n};\n\n// Generate random gradient for genre cards\nconst getRandomGradient = () => {\n const colors = [\n 'linear-gradient(135deg, #1DB954, #1ED760)',\n 'linear-gradient(135deg, #FF6B6B, #FFE66D)',\n 'linear-gradient(135deg, #4776E6, #8E54E9)',\n 'linear-gradient(135deg, #FF8008, #FFC837)',\n 'linear-gradient(135deg, #7F00FF, #E100FF)',\n 'linear-gradient(135deg, #11998E, #38EF7D)'\n ];\n \n return colors[Math.floor(Math.random() * colors.length)];\n};\n\n// Watch for URL query parameter changes\nonMounted(() => {\n const query = route.query.q;\n if (query) {\n searchQuery.value = query;\n searchActions.search(query);\n }\n});\n\nwatch(() => route.query.q, (newQuery) => {\n if (newQuery && newQuery !== searchQuery.value) {\n searchQuery.value = newQuery;\n searchActions.search(newQuery);\n } else if (!newQuery) {\n searchQuery.value = '';\n searchActions.clearSearch();\n }\n});\n</script>\n\n<style scoped>\n.bg-gradient-color {\n background: var(--gradient-color, linear-gradient(135deg, #1DB954, #1ED760));\n}\n</style>"],"names":["searchState","searchActions"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6LA,UAAM,QAAQ,SAAQ;AACtB,UAAM,SAAS,UAAS;AAGxB,UAAM,cAAc,IAAI,EAAE;AAC1B,UAAM,eAAe,IAAI,KAAK;AAG9B,UAAM,gBAAgB;AAAA,MACpB,EAAE,IAAI,OAAO,OAAO,MAAK;AAAA,MACzB,EAAE,IAAI,UAAU,OAAO,QAAO;AAAA,MAC9B,EAAE,IAAI,WAAW,OAAO,UAAS;AAAA,MACjC,EAAE,IAAI,UAAU,OAAO,SAAQ;AAAA,MAC/B,EAAE,IAAI,aAAa,OAAO,YAAW;AAAA,MACrC,EAAE,IAAI,UAAU,OAAO,SAAQ;AAAA,IACjC;AAGA,UAAM,YAAY,SAAS,MAAMA,MAAY,SAAS;AACtD,UAAM,cAAc,SAAS,MAAMA,MAAY,KAAK;AACpD,UAAM,aAAa,SAAS,MAAM;AAChC,aAAO,aAAa,MAAM,SAAS,KAC5B,cAAc,MAAM,SAAS,KAC7B,aAAa,MAAM,SAAS,KAC5B,gBAAgB,MAAM,SAAS,KAC/B,aAAa,MAAM,SAAS;AAAA,IACrC,CAAC;AAED,UAAM,eAAe,SAAS,MAAMA,MAAY,QAAQ,UAAU,CAAA,CAAE;AACpE,UAAM,gBAAgB,SAAS,MAAMA,MAAY,QAAQ,WAAW,CAAA,CAAE;AACtE,UAAM,eAAe,SAAS,MAAMA,MAAY,QAAQ,UAAU,CAAA,CAAE;AACpE,UAAM,kBAAkB,SAAS,MAAMA,MAAY,QAAQ,aAAa,CAAA,CAAE;AAC1E,UAAM,eAAe,SAAS,MAAMA,MAAY,QAAQ,UAAU,CAAA,CAAE;AAGpE,UAAM,eAAe,CAAC,UAAU;AAC9B,kBAAY,QAAQ;AAEpB,UAAI,MAAM,QAAQ;AAEhB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,OAAO,EAAE,GAAG,MAAK;AAAA,UACjB,SAAS;AAAA,QACf,CAAK;AAGDC,gBAAc,OAAO,KAAK;AAAA,MAC5B,OAAO;AAEL,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,SAAS;AAAA,QACf,CAAK;AACDA,gBAAc,YAAW;AAAA,MAC3B;AAAA,IACF;AAEA,UAAM,kBAAkB,CAAC,WAAW;AAClC,mBAAa,QAAQ;AACrBA,cAAc,UAAU,MAAM;AAAA,IAChC;AAGA,UAAM,oBAAoB,MAAM;AAC9B,YAAM,SAAS;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ;AAEE,aAAO,OAAO,KAAK,MAAM,KAAK,WAAW,OAAO,MAAM,CAAC;AAAA,IACzD;AAGA,cAAU,MAAM;AACd,YAAM,QAAQ,MAAM,MAAM;AAC1B,UAAI,OAAO;AACT,oBAAY,QAAQ;AACpBA,gBAAc,OAAO,KAAK;AAAA,MAC5B;AAAA,IACF,CAAC;AAED,UAAM,MAAM,MAAM,MAAM,GAAG,CAAC,aAAa;AACvC,UAAI,YAAY,aAAa,YAAY,OAAO;AAC9C,oBAAY,QAAQ;AACpBA,gBAAc,OAAO,QAAQ;AAAA,MAC/B,WAAW,CAAC,UAAU;AACpB,oBAAY,QAAQ;AACpBA,gBAAc,YAAW;AAAA,MAC3B;AAAA,IACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"SearchResults.vue.js","sources":["../../../../../../../src/modules/music/components/pages/SearchResults.vue"],"sourcesContent":["<!-- components/pages/SearchResults.vue -->\n<template>\n <div class=\"search-results-page pd-medium\">\n <div class=\"search-header mn-b-medium\">\n <h1 class=\"mn-b-small\">Search</h1>\n \n <BlockSearch \n :placeholder=\"'What do you want to listen to?'\"\n class=\"bg-light w-m-40r\"\n @search=\"handleSearch\"\n :autofocus=\"true\"\n />\n \n <div v-if=\"searchQuery\" class=\"search-filters flex gap-small mn-t-medium\">\n <Button \n v-for=\"filter in searchFilters\"\n :key=\"filter.id\"\n @click=\"setActiveFilter(filter.id)\"\n :class=\"[\n filter.id === activeFilter ? 'bg-white t-black' : 'bg-white-transp-50 hover-bg-white',\n ]\"\n class=\"radius-extra pd-small\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n {{ filter.label }}\n </Button>\n </div>\n </div>\n \n <div v-if=\"isLoading\" class=\"search-loading\">\n <Loader />\n </div>\n \n <div v-else-if=\"searchError\" class=\"search-error t-center pd-big\">\n <p class=\"t-fourth t-medium\">{{ searchError }}</p>\n </div>\n \n <div v-else-if=\"!searchQuery\" class=\"search-empty t-center pd-big\">\n <h2 class=\"mn-b-small\">Search for music</h2>\n <p class=\"t-transp t-medium\">Find your favorite songs, artists, albums, and playlists</p>\n </div>\n \n <div v-else-if=\"!hasResults\" class=\"search-no-results t-center pd-big\">\n <h2 class=\"mn-b-small\">No results found for \"{{ searchQuery }}\"</h2>\n <p class=\"t-transp t-medium\">Please try different keywords or check your spelling</p>\n </div>\n \n <div v-else class=\"search-results\">\n <!-- Songs Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'tracks') && trackResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Songs</h2>\n <Button \n v-if=\"trackResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('tracks')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in (activeFilter === 'all' ? trackResults.slice(0, 5) : trackResults)\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </section>\n \n <!-- Artists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'artists') && artistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Artists</h2>\n <Button \n v-if=\"artistResults.length > 6 && activeFilter === 'all'\"\n @click=\"setActiveFilter('artists')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"artists-grid cols-6 mobile:cols-3 gap-small\">\n <div v-for=\"artist in (activeFilter === 'all' ? artistResults.slice(0, 6) : artistResults)\" :key=\"artist._id\">\n <ArtistCard :artist=\"artist\" />\n </div>\n </div>\n </section>\n \n <!-- Albums Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'albums') && albumResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Albums</h2>\n <Button \n v-if=\"albumResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('albums')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"albums-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"album in (activeFilter === 'all' ? albumResults.slice(0, 5) : albumResults)\" :key=\"album._id\">\n <AlbumCard :album=\"album\" />\n </div>\n </div>\n </section>\n \n <!-- Playlists Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'playlists') && playlistResults.length > 0\" class=\"search-section mn-b-medium\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Playlists</h2>\n <Button \n v-if=\"playlistResults.length > 5 && activeFilter === 'all'\"\n @click=\"setActiveFilter('playlists')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"playlists-grid cols-5 mobile:cols-2 gap-small\">\n <div v-for=\"playlist in (activeFilter === 'all' ? playlistResults.slice(0, 5) : playlistResults)\" :key=\"playlist._id\">\n <PlaylistCard :playlist=\"playlist\" />\n </div>\n </div>\n </section>\n \n <!-- Genres Results -->\n <section v-if=\"(activeFilter === 'all' || activeFilter === 'genres') && genreResults.length > 0\" class=\"search-section\">\n <div class=\"flex-between flex mn-b-small\">\n <h2 class=\"\">Genres</h2>\n <Button \n v-if=\"genreResults.length > 4 && activeFilter === 'all'\"\n @click=\"setActiveFilter('genres')\"\n class=\"t-main bg-transparent border-none hover-bg-white-transp-10 pd-thin\"\n :showLoader=\"false\" \n :showSucces=\"false\"\n >\n See all\n </Button>\n </div>\n \n <div class=\"genres-grid cols-4 mobile:cols-2 gap-small\">\n <router-link \n v-for=\"genre in (activeFilter === 'all' ? genreResults.slice(0, 4) : genreResults)\" \n :key=\"genre._id\"\n :to=\"{ name: 'genre-detail', params: { url: genre.url } }\"\n class=\"genre-card bg-gradient-color pd-medium radius-medium t-center hover-scale-1 transition-cubic-in-out\"\n :style=\"{ \n '--gradient-color': getRandomGradient() \n }\"\n >\n <h3 class=\"\">{{ genre.name }}</h3>\n </router-link>\n </div>\n </section>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport BlockSearch from '@martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue';\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport AlbumCard from '../cards/AlbumCard.vue';\nimport PlaylistCard from '../cards/PlaylistCard.vue';\nimport ArtistCard from '../cards/ArtistCard.vue';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\n\n// Import search store\nimport { state as searchState, actions as searchActions } from '../../store/search.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// State\nconst searchQuery = ref('');\nconst activeFilter = ref('all');\n\n// Array of search filters\nconst searchFilters = [\n { id: 'all', label: 'All' },\n { id: 'tracks', label: 'Songs' },\n { id: 'artists', label: 'Artists' },\n { id: 'albums', label: 'Albums' },\n { id: 'playlists', label: 'Playlists' },\n { id: 'genres', label: 'Genres' }\n];\n\n// Computed properties\nconst isLoading = computed(() => searchState.isLoading);\nconst searchError = computed(() => searchState.error);\nconst hasResults = computed(() => {\n return trackResults.value.length > 0 || \n artistResults.value.length > 0 || \n albumResults.value.length > 0 || \n playlistResults.value.length > 0 || \n genreResults.value.length > 0;\n});\n\nconst trackResults = computed(() => searchState.results.tracks || []);\nconst artistResults = computed(() => searchState.results.artists || []);\nconst albumResults = computed(() => searchState.results.albums || []);\nconst playlistResults = computed(() => searchState.results.playlists || []);\nconst genreResults = computed(() => searchState.results.genres || []);\n\n// Methods\nconst handleSearch = (query) => {\n searchQuery.value = query;\n \n if (query.trim()) {\n // Update URL without reloading the page\n router.push({ \n name: 'music-search', \n query: { q: query },\n replace: true\n });\n \n // Perform search\n searchActions.search(query);\n } else {\n // Clear search when query is empty\n router.push({ \n name: 'music-search',\n replace: true\n });\n searchActions.clearSearch();\n }\n};\n\nconst setActiveFilter = (filter) => {\n activeFilter.value = filter;\n searchActions.setFilter(filter);\n};\n\n// Generate random gradient for genre cards\nconst getRandomGradient = () => {\n const colors = [\n 'linear-gradient(135deg, #1DB954, #1ED760)',\n 'linear-gradient(135deg, #FF6B6B, #FFE66D)',\n 'linear-gradient(135deg, #4776E6, #8E54E9)',\n 'linear-gradient(135deg, #FF8008, #FFC837)',\n 'linear-gradient(135deg, #7F00FF, #E100FF)',\n 'linear-gradient(135deg, #11998E, #38EF7D)'\n ];\n \n return colors[Math.floor(Math.random() * colors.length)];\n};\n\n// Watch for URL query parameter changes\nonMounted(() => {\n const query = route.query.q;\n if (query) {\n searchQuery.value = query;\n searchActions.search(query);\n }\n});\n\nwatch(() => route.query.q, (newQuery) => {\n if (newQuery && newQuery !== searchQuery.value) {\n searchQuery.value = newQuery;\n searchActions.search(newQuery);\n } else if (!newQuery) {\n searchQuery.value = '';\n searchActions.clearSearch();\n }\n});\n</script>\n\n<style scoped>\n.bg-gradient-color {\n background: var(--gradient-color, linear-gradient(135deg, #1DB954, #1ED760));\n}\n</style>"],"names":["searchState","searchActions"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6LA,UAAM,QAAQ,SAAQ;AACtB,UAAM,SAAS,UAAS;AAGxB,UAAM,cAAc,IAAI,EAAE;AAC1B,UAAM,eAAe,IAAI,KAAK;AAG9B,UAAM,gBAAgB;AAAA,MACpB,EAAE,IAAI,OAAO,OAAO,MAAK;AAAA,MACzB,EAAE,IAAI,UAAU,OAAO,QAAO;AAAA,MAC9B,EAAE,IAAI,WAAW,OAAO,UAAS;AAAA,MACjC,EAAE,IAAI,UAAU,OAAO,SAAQ;AAAA,MAC/B,EAAE,IAAI,aAAa,OAAO,YAAW;AAAA,MACrC,EAAE,IAAI,UAAU,OAAO,SAAQ;AAAA,IACjC;AAGA,UAAM,YAAY,SAAS,MAAMA,MAAY,SAAS;AACtD,UAAM,cAAc,SAAS,MAAMA,MAAY,KAAK;AACpD,UAAM,aAAa,SAAS,MAAM;AAChC,aAAO,aAAa,MAAM,SAAS,KAC5B,cAAc,MAAM,SAAS,KAC7B,aAAa,MAAM,SAAS,KAC5B,gBAAgB,MAAM,SAAS,KAC/B,aAAa,MAAM,SAAS;AAAA,IACrC,CAAC;AAED,UAAM,eAAe,SAAS,MAAMA,MAAY,QAAQ,UAAU,CAAA,CAAE;AACpE,UAAM,gBAAgB,SAAS,MAAMA,MAAY,QAAQ,WAAW,CAAA,CAAE;AACtE,UAAM,eAAe,SAAS,MAAMA,MAAY,QAAQ,UAAU,CAAA,CAAE;AACpE,UAAM,kBAAkB,SAAS,MAAMA,MAAY,QAAQ,aAAa,CAAA,CAAE;AAC1E,UAAM,eAAe,SAAS,MAAMA,MAAY,QAAQ,UAAU,CAAA,CAAE;AAGpE,UAAM,eAAe,CAAC,UAAU;AAC9B,kBAAY,QAAQ;AAEpB,UAAI,MAAM,QAAQ;AAEhB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,OAAO,EAAE,GAAG,MAAK;AAAA,UACjB,SAAS;AAAA,QACf,CAAK;AAGDC,gBAAc,OAAO,KAAK;AAAA,MAC5B,OAAO;AAEL,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,SAAS;AAAA,QACf,CAAK;AACDA,gBAAc,YAAW;AAAA,MAC3B;AAAA,IACF;AAEA,UAAM,kBAAkB,CAAC,WAAW;AAClC,mBAAa,QAAQ;AACrBA,cAAc,UAAU,MAAM;AAAA,IAChC;AAGA,UAAM,oBAAoB,MAAM;AAC9B,YAAM,SAAS;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ;AAEE,aAAO,OAAO,KAAK,MAAM,KAAK,WAAW,OAAO,MAAM,CAAC;AAAA,IACzD;AAGA,cAAU,MAAM;AACd,YAAM,QAAQ,MAAM,MAAM;AAC1B,UAAI,OAAO;AACT,oBAAY,QAAQ;AACpBA,gBAAc,OAAO,KAAK;AAAA,MAC5B;AAAA,IACF,CAAC;AAED,UAAM,MAAM,MAAM,MAAM,GAAG,CAAC,aAAa;AACvC,UAAI,YAAY,aAAa,YAAY,OAAO;AAC9C,oBAAY,QAAQ;AACpBA,gBAAc,OAAO,QAAQ;AAAA,MAC/B,WAAW,CAAC,UAAU;AACpB,oBAAY,QAAQ;AACpBA,gBAAc,YAAW;AAAA,MAC3B;AAAA,IACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|