@ozdao/martyrs 0.2.492 → 0.2.493
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 +52 -45
- package/dist/builder.js +53 -46
- package/dist/{crud-B-kQw3Z5.cjs → crud-JN_LFj01.cjs} +3 -0
- package/dist/{crud-Cwx5VlSm.js → crud-sE7GLPbj.js} +3 -0
- package/dist/globals.server.cjs +322 -3
- package/dist/globals.server.js +303 -1
- package/dist/{globals.verifier-D68mHEBl.cjs → globals.verifier-C0zj_LLo.cjs} +8 -1
- package/dist/{globals.verifier-CWFz5Gh2.js → globals.verifier-DFqKQ7hK.js} +8 -1
- package/dist/inventory.server.cjs +2 -2
- package/dist/inventory.server.js +2 -2
- package/dist/{main-SZQ1QjeP.js → main-CJm5myDI.js} +631 -607
- package/dist/{main-MzmGbSxs.cjs → main-DTaE01lg.cjs} +6 -6
- package/dist/martyrs/src/components/Calendar/Calendar.vue2.cjs +1 -1
- package/dist/martyrs/src/components/Calendar/Calendar.vue2.cjs.map +1 -1
- package/dist/martyrs/src/components/Calendar/Calendar.vue2.js +1 -1
- package/dist/martyrs/src/components/Calendar/Calendar.vue2.js.map +1 -1
- package/dist/martyrs/src/components/Feed/Feed.vue.cjs +33 -7
- package/dist/martyrs/src/components/Feed/Feed.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/Feed/Feed.vue.js +33 -7
- package/dist/martyrs/src/components/Feed/Feed.vue.js.map +1 -1
- package/dist/martyrs/src/components/Field/{Field.vue2.cjs → Field.vue.cjs} +2 -2
- package/dist/martyrs/src/components/Field/{Field.vue2.js.map → Field.vue.cjs.map} +1 -1
- package/dist/martyrs/src/components/Field/{Field.vue2.js → Field.vue.js} +2 -2
- package/dist/martyrs/src/components/Field/Field.vue.js.map +1 -0
- package/dist/martyrs/src/components/FieldBig/FieldBig.vue.cjs +1 -1
- package/dist/martyrs/src/components/FieldBig/FieldBig.vue.js +1 -1
- 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/components/Menu/{Menu.vue.cjs → Menu.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Menu/Menu.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Menu/{Menu.vue.js → Menu.vue2.js} +2 -2
- package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +1 -0
- package/dist/martyrs/src/components/Menu/MenuItem.vue.js +2 -2
- package/dist/martyrs/src/components/Menu/MenuItem.vue.js.map +1 -1
- package/dist/martyrs/src/components/PhotoViewer/PhotoViewer.vue.js +4 -4
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterCode.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterCode.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.cjs +2 -2
- package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +2 -2
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEditProfile.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEditProfile.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/sections/ProfileEditCredentials.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/sections/ProfileEditCredentials.vue.js +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs +2 -2
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js +2 -2
- package/dist/martyrs/src/modules/constructor/components/elements/Card.vue.cjs +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/Card.vue.js +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/Embed.vue.cjs +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/Embed.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.cjs +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EditEventTickets.vue.cjs +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EditEventTickets.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/pages/Event.vue.cjs +1 -1
- package/dist/martyrs/src/modules/events/components/pages/Event.vue.js +1 -1
- package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.cjs +1 -1
- package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +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/blocks/BlockSearch.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs +5 -1
- 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 +5 -1
- package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js.map +1 -1
- package/dist/martyrs/src/modules/icons/entities/IconTime.vue.cjs +2 -2
- package/dist/martyrs/src/modules/icons/entities/IconTime.vue.js +2 -2
- package/dist/martyrs/src/modules/icons/navigation/IconChevronLeft.vue.cjs +2 -2
- package/dist/martyrs/src/modules/icons/navigation/IconChevronLeft.vue.js +2 -2
- package/dist/martyrs/src/modules/icons/navigation/IconChevronRight.vue.cjs +2 -2
- package/dist/martyrs/src/modules/icons/navigation/IconChevronRight.vue.js +2 -2
- package/dist/martyrs/src/modules/icons/pages/IconsPage.vue.js +6 -6
- package/dist/martyrs/src/modules/icons/pages/IconsPage.vue.js.map +1 -1
- package/dist/martyrs/src/modules/inventory/components/forms/AdjustmentForm.vue.cjs +1 -1
- package/dist/martyrs/src/modules/inventory/components/forms/AdjustmentForm.vue.js +1 -1
- package/dist/martyrs/src/modules/inventory/components/forms/StockAlertsForm.vue.cjs +1 -1
- package/dist/martyrs/src/modules/inventory/components/forms/StockAlertsForm.vue.js +1 -1
- package/dist/martyrs/src/modules/inventory/components/pages/Inventory.vue.cjs +1 -1
- package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.cjs +22 -15
- package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js +23 -16
- package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.cjs +2 -2
- package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.js +2 -2
- package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.cjs +31 -13
- package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.js +33 -15
- package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs +39 -22
- 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 +39 -22
- package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/AlbumForm.vue.cjs +1 -1
- package/dist/martyrs/src/modules/music/components/forms/AlbumForm.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/forms/ArtistForm.vue.cjs +1 -1
- package/dist/martyrs/src/modules/music/components/forms/ArtistForm.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.cjs +385 -125
- package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.js +391 -131
- package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs +24 -7
- 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 +25 -8
- package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.cjs +99 -87
- package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.js +111 -99
- package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.cjs +21 -0
- package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.cjs.map +1 -0
- package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js +21 -0
- package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js.map +1 -0
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs +442 -210
- 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 +445 -213
- 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 +92 -117
- 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 +93 -118
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.cjs +72 -113
- package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js +78 -119
- package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs +15 -12
- 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 +15 -12
- 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 +558 -429
- 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 +560 -431
- 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 +146 -284
- 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 +149 -287
- 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 +460 -63
- 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 +462 -65
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.cjs +126 -136
- package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js +129 -139
- package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.cjs +18 -15
- package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.js +18 -15
- package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.cjs +28 -23
- package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js +29 -24
- package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/music.client.cjs +3 -6
- package/dist/martyrs/src/modules/music/music.client.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/music.client.js +9 -12
- package/dist/martyrs/src/modules/music/music.client.js.map +1 -1
- package/dist/martyrs/src/modules/music/router/music.cjs +27 -1
- package/dist/martyrs/src/modules/music/router/music.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/router/music.js +27 -1
- package/dist/martyrs/src/modules/music/router/music.js.map +1 -1
- package/dist/martyrs/src/modules/music/store/artists.cjs +6 -4
- package/dist/martyrs/src/modules/music/store/artists.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/store/artists.js +6 -4
- package/dist/martyrs/src/modules/music/store/artists.js.map +1 -1
- package/dist/martyrs/src/modules/music/store/player.cjs +5 -0
- package/dist/martyrs/src/modules/music/store/player.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/store/player.js +5 -0
- package/dist/martyrs/src/modules/music/store/player.js.map +1 -1
- package/dist/martyrs/src/modules/music/store/tracks.cjs +22 -0
- package/dist/martyrs/src/modules/music/store/tracks.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/store/tracks.js +22 -0
- package/dist/martyrs/src/modules/music/store/tracks.js.map +1 -1
- package/dist/martyrs/src/modules/orders/components/blocks/CardOrderBackoffice.vue.js +2 -2
- package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.js +2 -2
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormAddCustomer.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormAddCustomer.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormCustomerDetails.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormCustomerDetails.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.js +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/DepartmentEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/DepartmentEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Documents.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Documents.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/MembersAdd.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/MembersAdd.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/router/organizations.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/router/organizations.js +1 -1
- package/dist/martyrs/src/modules/pages/views/components/blocks/CardPage.vue.cjs +1 -1
- package/dist/martyrs/src/modules/pages/views/components/blocks/CardPage.vue.js +1 -1
- package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/sections/EditAttributes.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/sections/EditAttributes.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/sections/EditDiscounts.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/sections/EditDiscounts.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/sections/EditVariants.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/sections/EditVariants.vue.js +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.cjs +4 -3
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.js +4 -3
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.js.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.cjs +37 -70
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.js +38 -71
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.js.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs +2 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +6 -5
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.cjs +45 -52
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js +46 -53
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/blocks/SpotMemberModify.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/blocks/SpotMemberModify.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/sections/WorktimeEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/sections/WorktimeEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/wallet/views/components/blocks/CardDeposit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/wallet/views/components/blocks/CardDeposit.vue.js +1 -1
- package/dist/martyrs/src/modules/wallet/views/components/blocks/CryptoDeposit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/wallet/views/components/blocks/CryptoDeposit.vue.js +1 -1
- package/dist/martyrs.cjs.js +1 -1
- package/dist/martyrs.css +1 -1
- package/dist/martyrs.es.js +1 -1
- package/dist/music.server.cjs +124 -31
- package/dist/music.server.js +124 -31
- package/dist/organizations.server.cjs +1 -1
- package/dist/organizations.server.js +1 -1
- package/dist/products.server.cjs +2 -2
- package/dist/products.server.js +2 -2
- package/dist/rents.server.cjs +3 -3
- package/dist/rents.server.js +3 -3
- package/dist/style.css +373 -80
- package/dist/{web-D7lZjuC0.js → web-Dkk0_7TA.js} +1 -1
- package/dist/{web-D-YZ9KHz.cjs → web-stVkXd0l.cjs} +1 -1
- package/package.json +1 -1
- package/src/builder/modes/ssr.prod.js +21 -5
- package/src/builder/rspack/rspack.config.ssr.client.js +40 -40
- package/src/components/Calendar/Calendar.vue +378 -377
- package/src/components/Feed/Feed.vue +28 -2
- package/src/modules/globals/controllers/classes/crud/crud.policies.js +5 -0
- package/src/modules/globals/controllers/classes/globals.validator.js +8 -1
- package/src/modules/globals/views/components/layouts/Client.vue +7 -0
- package/src/modules/music/README.md +8 -0
- package/src/modules/music/components/SidebarMusic.vue +6 -9
- package/src/modules/music/components/cards/AlbumCard.vue +20 -14
- package/src/modules/music/components/cards/ArtistCard.vue +1 -1
- package/src/modules/music/components/cards/PlaylistCard.vue +31 -11
- package/src/modules/music/components/cards/TrackListCard.vue +24 -13
- package/src/modules/music/components/forms/PlaylistForm.vue +417 -107
- package/src/modules/music/components/forms/SearchForm.vue +31 -8
- package/src/modules/music/components/forms/TrackForm.vue +50 -32
- package/src/modules/music/components/layouts/MusicBottomPlayer.vue +17 -0
- package/src/modules/music/components/pages/Album.vue +373 -186
- package/src/modules/music/components/pages/Artist.vue +54 -94
- package/src/modules/music/components/pages/MusicHome.vue +59 -56
- package/src/modules/music/components/pages/MusicLibrary.vue +13 -11
- package/src/modules/music/components/pages/Playlist.vue +495 -379
- package/src/modules/music/components/pages/SearchResults.vue +185 -313
- package/src/modules/music/components/pages/Track.vue +363 -69
- package/src/modules/music/components/player/MusicPlayer.vue +368 -97
- package/src/modules/music/components/player/TrackProgress.vue +76 -22
- package/src/modules/music/components/player/VolumeControl.vue +61 -28
- package/src/modules/music/controllers/search.controller.js +3 -0
- package/src/modules/music/controllers/stream.controller.js +11 -3
- package/src/modules/music/middlewares/playlists.verifier.js +1 -1
- package/src/modules/music/music.client.js +3 -6
- package/src/modules/music/music.server.js +8 -4
- package/src/modules/music/router/music.js +8 -1
- package/src/modules/music/routes/albums.routes.js +37 -5
- package/src/modules/music/routes/artists.routes.js +14 -4
- package/src/modules/music/routes/genres.routes.js +5 -1
- package/src/modules/music/routes/playlists.routes.js +42 -9
- package/src/modules/music/routes/tracks.routes.js +27 -2
- package/src/modules/music/store/artists.js +6 -2
- package/src/modules/music/store/player.js +6 -0
- package/src/modules/music/store/tracks.js +31 -0
- package/src/modules/music/websocket/streaming.handler.js +7 -1
- package/src/modules/rents/controllers/services/rents.services.js +2 -2
- package/src/modules/rents/views/components/pages/Gant/GanttBar.vue +4 -3
- package/src/modules/rents/views/components/pages/Gant/GanttChart.vue +42 -40
- package/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue +3 -1
- package/src/modules/rents/views/components/pages/Rents.vue +60 -56
- package/dist/globals.websocket-DzvdIBf6.js +0 -306
- package/dist/globals.websocket-k6_B1T7k.cjs +0 -322
- package/dist/martyrs/src/components/Field/Field.vue2.cjs.map +0 -1
- package/dist/martyrs/src/components/Menu/Menu.vue.cjs.map +0 -1
- package/dist/martyrs/src/components/Menu/Menu.vue.js.map +0 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.cjs +0 -69
- package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.cjs.map +0 -1
- package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.js +0 -69
- package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.js.map +0 -1
- package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.cjs +0 -104
- package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.cjs.map +0 -1
- package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.js +0 -104
- package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.js.map +0 -1
- package/src/modules/music/components/cards/TrackCard.vue +0 -86
- package/src/modules/music/components/layouts/MusicLayout.vue +0 -83
|
@@ -1,394 +1,447 @@
|
|
|
1
|
-
<!-- components/pages/Playlist.vue -->
|
|
2
1
|
<template>
|
|
3
|
-
<div class="playlist-page">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
</div>
|
|
7
|
-
|
|
8
|
-
<div v-else-if="!playlist" class="t-center pd-big">
|
|
2
|
+
<div class="playlist-page pd-small">
|
|
3
|
+
<!-- Not Found -->
|
|
4
|
+
<div v-if="hasLoaded && !playlist" class="t-center pd-big">
|
|
9
5
|
<h2 class="">Playlist not found</h2>
|
|
10
|
-
<p class="t-
|
|
6
|
+
<p class="t-transp t-medium">The playlist you're looking for doesn't exist or has been removed.</p>
|
|
11
7
|
</div>
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
<!-- Playlist Content -->
|
|
10
|
+
<div v-if="playlist" class="playlist-content cols-2-fit-content mobile:cols-1 gap-big">
|
|
11
|
+
<!-- Left Column - Cover & Stats -->
|
|
12
|
+
<div class="pos-sticky pos-t-0 mobile:pos-relative playlist-cover-section">
|
|
13
|
+
<!-- Cover -->
|
|
14
|
+
<div class="cover-container relative mn-b-medium radius-big overflow-hidden shadow-big">
|
|
17
15
|
<Media
|
|
18
|
-
:url="playlist.coverUrl || '/assets/placeholder-playlist.jpg'"
|
|
19
|
-
|
|
16
|
+
:url="playlist.coverUrl || '/assets/placeholder-playlist.jpg'"
|
|
17
|
+
:alt="playlist.title"
|
|
18
|
+
class="aspect-1x1 w-100 radius-medium o-hidden"
|
|
20
19
|
/>
|
|
21
20
|
</div>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{{ playlist.description }}
|
|
21
|
+
|
|
22
|
+
<!-- Quick Stats -->
|
|
23
|
+
<div class="stats-grid grid cols-2 gap-small">
|
|
24
|
+
<div class="stat-card bg-light pd-medium radius-medium t-center">
|
|
25
|
+
<div class=" mn-b-thin">{{ playlistTracks.length }}</div>
|
|
26
|
+
<div class="t-small t-transp t-uppercase">Tracks</div>
|
|
29
27
|
</div>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
v-if="playlist.owner && playlist.owner.target"
|
|
34
|
-
:to="getOwnerProfileLink(playlist.owner)"
|
|
35
|
-
class=" t-medium hover-t-main"
|
|
36
|
-
>
|
|
37
|
-
{{ getPlaylistOwnerName(playlist) }}
|
|
38
|
-
</router-link>
|
|
39
|
-
<span v-else class=" t-medium">{{ getPlaylistOwnerName(playlist) }}</span>
|
|
40
|
-
|
|
41
|
-
<span class="t-grey mn-l-small mn-r-small">•</span>
|
|
42
|
-
|
|
43
|
-
<span class="t-grey">{{ playlistTracks.length }} {{ playlistTracks.length === 1 ? 'song' : 'songs' }}</span>
|
|
28
|
+
<div class="stat-card bg-light pd-medium radius-medium t-center">
|
|
29
|
+
<div class=" mn-b-thin">{{ formatNumber(playlist.followers || 0) }}</div>
|
|
30
|
+
<div class="t-small t-transp t-uppercase">Followers</div>
|
|
44
31
|
</div>
|
|
45
32
|
</div>
|
|
46
33
|
</div>
|
|
47
|
-
|
|
48
|
-
<!-- Playlist
|
|
49
|
-
<div class="playlist-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class="
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class="
|
|
73
|
-
|
|
74
|
-
|
|
34
|
+
|
|
35
|
+
<!-- Right Column - Playlist Details -->
|
|
36
|
+
<div class="playlist-details-section">
|
|
37
|
+
<!-- Playlist Type Badge -->
|
|
38
|
+
<div class="flex items-center gap-small mn-b-small">
|
|
39
|
+
<span class="badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase">
|
|
40
|
+
Playlist
|
|
41
|
+
</span>
|
|
42
|
+
<span v-if="playlist.isCollaborative" class="badge bg-secondary-transp-20 t-secondary pd-thin-big radius-small t-small">
|
|
43
|
+
Collaborative
|
|
44
|
+
</span>
|
|
45
|
+
<span v-if="playlist.status === 'published'" class="badge bg-success-transp-20 t-success pd-thin-big radius-small t-small">
|
|
46
|
+
Published
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Playlist Title -->
|
|
51
|
+
<h1 class="h1 mn-b-medium">{{ playlist.title }}</h1>
|
|
52
|
+
|
|
53
|
+
<!-- Action Buttons -->
|
|
54
|
+
<div class="flex gap-small mn-b-medium">
|
|
55
|
+
<Button
|
|
56
|
+
@click="playPlaylist"
|
|
57
|
+
color="primary"
|
|
58
|
+
size="medium"
|
|
59
|
+
class="flex-1 flex-center gap-thin"
|
|
60
|
+
>
|
|
61
|
+
<IconPlay class="i-medium" />
|
|
62
|
+
Play All
|
|
63
|
+
</Button>
|
|
64
|
+
|
|
65
|
+
<Button
|
|
66
|
+
@click="shufflePlay"
|
|
67
|
+
color="primary"
|
|
68
|
+
size="medium"
|
|
69
|
+
class="flex-1 flex-center gap-thin"
|
|
75
70
|
>
|
|
76
|
-
<
|
|
71
|
+
<IconShuffle class="i-medium" />
|
|
72
|
+
Shuffle
|
|
77
73
|
</Button>
|
|
74
|
+
|
|
75
|
+
<Button
|
|
76
|
+
@click="toggleFollow"
|
|
77
|
+
color="primary"
|
|
78
|
+
size="medium"
|
|
79
|
+
class="flex-1 flex-center gap-thin"
|
|
80
|
+
>
|
|
81
|
+
{{isFollowing ? 'Follow' : 'Unfollow'}}
|
|
82
|
+
</Button>
|
|
83
|
+
|
|
78
84
|
|
|
79
|
-
<
|
|
80
|
-
<
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
>
|
|
88
|
-
|
|
89
|
-
</Button>
|
|
90
|
-
</li>
|
|
91
|
-
<li class="mn-b-thin">
|
|
92
|
-
<Button
|
|
93
|
-
@click="addToQueue"
|
|
94
|
-
class="bg-transparent border-none pd-thin w-100 t-left hover-bg-dark radius-small"
|
|
95
|
-
:showLoader="false"
|
|
96
|
-
:showSucces="false"
|
|
97
|
-
>
|
|
98
|
-
<span>Add to Queue</span>
|
|
99
|
-
</Button>
|
|
100
|
-
</li>
|
|
101
|
-
<li v-if="isOwner" class="mn-b-thin">
|
|
102
|
-
<Button
|
|
103
|
-
@click="toggleCollaborative"
|
|
104
|
-
class="bg-transparent border-none pd-thin w-100 t-left hover-bg-dark radius-small"
|
|
105
|
-
:showLoader="false"
|
|
106
|
-
:showSucces="false"
|
|
107
|
-
>
|
|
108
|
-
<span>{{ playlist.isCollaborative ? 'Make Private' : 'Make Collaborative' }}</span>
|
|
85
|
+
<Dropdown :label="{component: IconEllipsis, class: 'i-medium' }" v-model="showDropdown" class="relative">
|
|
86
|
+
<template #trigger>
|
|
87
|
+
<Button color="transp" size="medium" class="w-3r h-3r radius-full">
|
|
88
|
+
<IconEllipsis class="w-1-25r h-1-25r" />
|
|
89
|
+
</Button>
|
|
90
|
+
</template>
|
|
91
|
+
<template #default>
|
|
92
|
+
<div class="dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin">
|
|
93
|
+
<Button @click="addToQueue" color="transp" size="small" class="w-100 justify-start">
|
|
94
|
+
Add to Queue
|
|
109
95
|
</Button>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<Button
|
|
113
|
-
@click="deletePlaylist"
|
|
114
|
-
class="bg-transparent border-none pd-thin t-fourth w-100 t-left hover-bg-dark radius-small"
|
|
115
|
-
:showLoader="false"
|
|
116
|
-
:showSucces="false"
|
|
117
|
-
>
|
|
118
|
-
<span>Delete</span>
|
|
96
|
+
<Button @click="copyLink" color="transp" size="small" class="w-100 justify-start">
|
|
97
|
+
Copy Link
|
|
119
98
|
</Button>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<!-- Playlist Tracks -->
|
|
137
|
-
<div class="playlist-tracks">
|
|
138
|
-
<div v-if="playlistTracks.length === 0" class="empty-tracks t-center pd-big bg-dark-transp-10 radius-medium">
|
|
139
|
-
<h3 class=" mn-b-small">This playlist is empty</h3>
|
|
140
|
-
<p class="t-grey t-medium">Add some tracks to get started</p>
|
|
141
|
-
|
|
142
|
-
<Button
|
|
143
|
-
v-if="isOwner || isCollaborator"
|
|
144
|
-
@click="$router.push({ name: 'music-search' })"
|
|
145
|
-
class="bg-main radius-small pd-small mn-t-medium hover-scale-1"
|
|
146
|
-
:showLoader="false"
|
|
147
|
-
:showSucces="false"
|
|
148
|
-
>
|
|
149
|
-
Find Tracks
|
|
150
|
-
</Button>
|
|
99
|
+
<template v-if="isOwner || isCollaborator">
|
|
100
|
+
<hr class="mn-v-thin border-dark-transp-10" />
|
|
101
|
+
<Button @click="editPlaylist" color="transp" size="small" class="w-100 justify-start">
|
|
102
|
+
Edit Playlist
|
|
103
|
+
</Button>
|
|
104
|
+
<Button v-if="isOwner" @click="toggleCollaborative" color="transp" size="small" class="w-100 justify-start">
|
|
105
|
+
{{ playlist.isCollaborative ? 'Make Private' : 'Make Collaborative' }}
|
|
106
|
+
</Button>
|
|
107
|
+
<Button v-if="isOwner" @click="deletePlaylist" color="danger" size="small" class="w-100 justify-start">
|
|
108
|
+
Delete Playlist
|
|
109
|
+
</Button>
|
|
110
|
+
</template>
|
|
111
|
+
</div>
|
|
112
|
+
</template>
|
|
113
|
+
</Dropdown>
|
|
151
114
|
</div>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
empty: {
|
|
163
|
-
title: 'No tracks in playlist',
|
|
164
|
-
description: 'Add some tracks to get started',
|
|
165
|
-
class: 'pd-medium bg-dark-transp-10 radius-medium'
|
|
166
|
-
}
|
|
167
|
-
}"
|
|
168
|
-
class="gap-thin"
|
|
169
|
-
>
|
|
170
|
-
<template #default="{ items }">
|
|
171
|
-
<TrackCard
|
|
172
|
-
v-for="track in items"
|
|
173
|
-
:key="track._id"
|
|
174
|
-
:track="track"
|
|
175
|
-
:showAlbum="true"
|
|
176
|
-
:showCover="true"
|
|
177
|
-
class="w-100 bg-dark-transp-10 radius-medium"
|
|
178
|
-
/>
|
|
179
|
-
</template>
|
|
180
|
-
</Feed>
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
<!-- Edit Playlist Modal -->
|
|
184
|
-
<Popup
|
|
185
|
-
v-if="showEditModal && isOwner"
|
|
186
|
-
@close-popup="showEditModal = false"
|
|
187
|
-
class="bg-dark pd-small w-m-25r radius-medium "
|
|
188
|
-
>
|
|
189
|
-
<h3 class="mn-b-medium">Edit Playlist</h3>
|
|
190
|
-
|
|
191
|
-
<form @submit.prevent="updatePlaylist">
|
|
192
|
-
<div class="form-group mn-b-medium">
|
|
193
|
-
<label for="title" class=" t-medium mn-b-thin d-block">Playlist Name</label>
|
|
194
|
-
<Field
|
|
195
|
-
v-model:field="editForm.title"
|
|
196
|
-
id="title"
|
|
197
|
-
type="text"
|
|
198
|
-
placeholder="Playlist Name"
|
|
199
|
-
:validation="validationErrors.title"
|
|
200
|
-
class="w-100 pd-small bg-dark-transp-25 radius-small "
|
|
201
|
-
/>
|
|
202
|
-
</div>
|
|
203
|
-
|
|
204
|
-
<div class="form-group mn-b-medium">
|
|
205
|
-
<label for="description" class=" t-medium mn-b-thin d-block">Description</label>
|
|
206
|
-
<Field
|
|
207
|
-
v-model:field="editForm.description"
|
|
208
|
-
id="description"
|
|
209
|
-
type="textarea"
|
|
210
|
-
placeholder="Add an optional description"
|
|
211
|
-
class="w-100 pd-small bg-dark-transp-25 radius-small "
|
|
212
|
-
/>
|
|
213
|
-
</div>
|
|
214
|
-
|
|
215
|
-
<div class="form-group mn-b-medium">
|
|
216
|
-
<label class=" t-medium mn-b-thin d-block">Privacy</label>
|
|
217
|
-
<div class="flex gap-small">
|
|
218
|
-
<Radio
|
|
219
|
-
v-model:radio="editForm.isPublic"
|
|
220
|
-
:value="true"
|
|
221
|
-
name="privacy"
|
|
222
|
-
label="Public"
|
|
223
|
-
class=""
|
|
224
|
-
/>
|
|
225
|
-
<Radio
|
|
226
|
-
v-model:radio="editForm.isPublic"
|
|
227
|
-
:value="false"
|
|
228
|
-
name="privacy"
|
|
229
|
-
label="Private"
|
|
230
|
-
class=""
|
|
231
|
-
/>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
<div class="form-group mn-b-medium">
|
|
236
|
-
<label class=" t-medium mn-b-thin d-block">Cover Image</label>
|
|
237
|
-
<div class="playlist-cover-upload flex gap-medium">
|
|
238
|
-
<div class="playlist-cover-preview bg-dark-transp-25 radius-small o-hidden">
|
|
115
|
+
|
|
116
|
+
<!-- Owner/Creator Card -->
|
|
117
|
+
<div class="owner-section mn-b-big">
|
|
118
|
+
<h3 class="t-medium mn-b-small">Created by</h3>
|
|
119
|
+
<div class="owner-card bg-light pd-medium radius-medium flex items-center gap-medium">
|
|
120
|
+
<router-link
|
|
121
|
+
:to="getOwnerProfileLink(playlist.creator || playlist.owner)"
|
|
122
|
+
class="flex items-center gap-medium flex-1 hover-opacity"
|
|
123
|
+
>
|
|
124
|
+
<div class="owner-avatar">
|
|
239
125
|
<Media
|
|
240
|
-
v-if="
|
|
241
|
-
:url="
|
|
242
|
-
|
|
126
|
+
v-if="getOwnerData(playlist)?.photoUrl"
|
|
127
|
+
:url="getOwnerData(playlist).photoUrl"
|
|
128
|
+
:alt="getOwnerData(playlist)?.name"
|
|
129
|
+
class="w-4r h-4r radius-full object-cover"
|
|
243
130
|
/>
|
|
244
|
-
<div v-else class="w-
|
|
245
|
-
|
|
131
|
+
<div v-else class="w-4r h-4r radius-full bg-primary flex-center ">
|
|
132
|
+
{{ getPlaylistOwnerName(playlist).charAt(0) }}
|
|
246
133
|
</div>
|
|
247
134
|
</div>
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<div class="form-actions t-right">
|
|
135
|
+
<div>
|
|
136
|
+
<div class="flex items-center gap-thin">
|
|
137
|
+
<span class="t-large ">{{ getPlaylistOwnerName(playlist) }}</span>
|
|
138
|
+
<IconVerified v-if="getOwnerData(playlist)?.isVerified" class="w-1r h-1r t-primary" />
|
|
139
|
+
</div>
|
|
140
|
+
<span class="t-small t-transp">{{ playlist.creator?.type || 'User' }}</span>
|
|
141
|
+
</div>
|
|
142
|
+
</router-link>
|
|
258
143
|
<Button
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
:showSucces="false"
|
|
144
|
+
v-if="!isOwner && authState.user"
|
|
145
|
+
@click="() => toggleFollowUser(getOwnerId(playlist))"
|
|
146
|
+
:color="followedUsers.includes(getOwnerId(playlist)) ? 'primary' : 'transp'"
|
|
147
|
+
size="small"
|
|
264
148
|
>
|
|
265
|
-
|
|
149
|
+
{{ followedUsers.includes(getOwnerId(playlist)) ? 'Following' : 'Follow' }}
|
|
266
150
|
</Button>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<!-- Collaborators -->
|
|
155
|
+
<div v-if="playlist.collaborators && playlist.collaborators.length > 0" class="collaborators-section mn-b-big">
|
|
156
|
+
<h3 class="t-medium mn-b-small">Collaborators</h3>
|
|
157
|
+
<div class="flex flex-wrap gap-small">
|
|
158
|
+
<div
|
|
159
|
+
v-for="collaborator in playlist.collaborators"
|
|
160
|
+
:key="collaborator._id || collaborator"
|
|
161
|
+
class="collaborator-chip bg-light pd-thin-big radius-full flex items-center gap-thin"
|
|
275
162
|
>
|
|
276
|
-
|
|
277
|
-
|
|
163
|
+
<Media
|
|
164
|
+
v-if="collaborator.photoUrl"
|
|
165
|
+
:url="collaborator.photoUrl"
|
|
166
|
+
class="w-1-5r h-1-5r radius-full object-cover"
|
|
167
|
+
/>
|
|
168
|
+
<span class="t-small">{{ collaborator.name || collaborator.profile?.name || 'User' }}</span>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- Metadata Cards -->
|
|
174
|
+
<div class="metadata-grid grid cols-2 gap-small mn-b-big">
|
|
175
|
+
<!-- Created Date -->
|
|
176
|
+
<div class="metadata-card bg-light pd-medium radius-medium flex items-center gap-medium">
|
|
177
|
+
<IconCalendar class="i-medium t-primary" />
|
|
178
|
+
<div>
|
|
179
|
+
<div class="t-small t-transp t-uppercase">Created</div>
|
|
180
|
+
<div class="t-medium ">{{ formatDate(playlist.createdAt) }}</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<!-- Total Duration -->
|
|
185
|
+
<div class="metadata-card bg-light pd-medium radius-medium flex items-center gap-medium">
|
|
186
|
+
<IconClock class="i-medium t-primary" />
|
|
187
|
+
<div>
|
|
188
|
+
<div class="t-small t-transp t-uppercase">Duration</div>
|
|
189
|
+
<div class="t-medium ">{{ totalDuration }}</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<!-- Updated Date -->
|
|
194
|
+
<div class="metadata-card bg-light pd-medium radius-medium flex items-center gap-medium">
|
|
195
|
+
<IconRefresh class="i-medium t-primary" />
|
|
196
|
+
<div>
|
|
197
|
+
<div class="t-small t-transp t-uppercase">Updated</div>
|
|
198
|
+
<div class="t-medium ">{{ formatDate(playlist.updatedAt) }}</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<!-- Visibility -->
|
|
203
|
+
<div class="metadata-card bg-light pd-medium radius-medium flex items-center gap-medium">
|
|
204
|
+
<IconEye class="i-medium t-primary" />
|
|
205
|
+
<div>
|
|
206
|
+
<div class="t-small t-transp t-uppercase">Visibility</div>
|
|
207
|
+
<div class="t-medium ">{{ playlist.isPublic ? 'Public' : 'Private' }}</div>
|
|
208
|
+
</div>
|
|
278
209
|
</div>
|
|
279
|
-
</form>
|
|
280
|
-
</Popup>
|
|
281
|
-
|
|
282
|
-
<!-- Delete Confirmation Modal -->
|
|
283
|
-
<Popup
|
|
284
|
-
v-if="showDeleteModal"
|
|
285
|
-
@close-popup="showDeleteModal = false"
|
|
286
|
-
class="bg-dark pd-small w-m-25r radius-medium "
|
|
287
|
-
>
|
|
288
|
-
<h3 class="mn-b-medium">Delete Playlist</h3>
|
|
289
|
-
<p class="t-grey mn-b-medium">Are you sure you want to delete this playlist? This action cannot be undone.</p>
|
|
290
|
-
|
|
291
|
-
<div class="t-right">
|
|
292
|
-
<Button
|
|
293
|
-
@click="showDeleteModal = false"
|
|
294
|
-
class="bg-dark-transp-25 pd-small radius-small mn-r-small hover-bg-dark"
|
|
295
|
-
:showLoader="false"
|
|
296
|
-
:showSucces="false"
|
|
297
|
-
>
|
|
298
|
-
Cancel
|
|
299
|
-
</Button>
|
|
300
|
-
|
|
301
|
-
<Button
|
|
302
|
-
@click="confirmDelete"
|
|
303
|
-
class="bg-fourth pd-small radius-small hover-scale-1"
|
|
304
|
-
:showLoader="true"
|
|
305
|
-
:showSucces="true"
|
|
306
|
-
>
|
|
307
|
-
Delete
|
|
308
|
-
</Button>
|
|
309
210
|
</div>
|
|
310
|
-
|
|
211
|
+
|
|
212
|
+
<!-- Tags -->
|
|
213
|
+
<div v-if="playlist.tags && playlist.tags.length" class="tags-section mn-b-medium">
|
|
214
|
+
<h3 class="t-medium mn-b-small">Tags</h3>
|
|
215
|
+
<div class="flex gap-thin flex-wrap">
|
|
216
|
+
<span
|
|
217
|
+
v-for="tag in playlist.tags"
|
|
218
|
+
:key="tag"
|
|
219
|
+
class="tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer"
|
|
220
|
+
>
|
|
221
|
+
#{{ tag }}
|
|
222
|
+
</span>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- Description -->
|
|
227
|
+
<div v-if="playlist.description" class="description-section bg-light pd-medium radius-medium mn-b-medium">
|
|
228
|
+
<h3 class="t-medium mn-b-small">About</h3>
|
|
229
|
+
<p class="t-transp">{{ playlist.description }}</p>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
311
232
|
</div>
|
|
233
|
+
|
|
234
|
+
<!-- Playlist Tracks -->
|
|
235
|
+
<section v-if="!isLoading && playlist && playlistTracks.length" class="tracks-section mn-t-big">
|
|
236
|
+
<h2 class="h2 mn-b-medium">Tracklist</h2>
|
|
237
|
+
<Feed
|
|
238
|
+
:store="{
|
|
239
|
+
read: () => Promise.resolve(playlistTracks),
|
|
240
|
+
state: { isLoading: false }
|
|
241
|
+
}"
|
|
242
|
+
:external="true"
|
|
243
|
+
:items="playlistTracks"
|
|
244
|
+
:states="{
|
|
245
|
+
empty: {
|
|
246
|
+
title: 'No tracks in playlist',
|
|
247
|
+
description: 'Add some tracks to get started',
|
|
248
|
+
class: 'pd-medium t-center'
|
|
249
|
+
}
|
|
250
|
+
}"
|
|
251
|
+
>
|
|
252
|
+
<template #default="{ items }">
|
|
253
|
+
<div class="bg-light radius-medium o-hidden">
|
|
254
|
+
<TrackListCard
|
|
255
|
+
v-for="(track, index) in items"
|
|
256
|
+
:key="track._id"
|
|
257
|
+
:track="track"
|
|
258
|
+
:index="index + 1"
|
|
259
|
+
:showAlbum="true"
|
|
260
|
+
:showCover="true"
|
|
261
|
+
:canRemove="isOwner || isCollaborator"
|
|
262
|
+
@remove="() => removeTrack(track._id)"
|
|
263
|
+
/>
|
|
264
|
+
</div>
|
|
265
|
+
</template>
|
|
266
|
+
</Feed>
|
|
267
|
+
</section>
|
|
268
|
+
|
|
269
|
+
<!-- Empty State -->
|
|
270
|
+
<section v-else-if="!isLoading && playlist && !playlistTracks.length" class="empty-section mn-t-big">
|
|
271
|
+
<div class="empty-tracks t-center pd-big bg-light radius-medium">
|
|
272
|
+
<h3 class=" mn-b-small">This playlist is empty</h3>
|
|
273
|
+
<p class="t-transp t-medium mn-b-medium">Add some tracks to get started</p>
|
|
274
|
+
|
|
275
|
+
<Button
|
|
276
|
+
v-if="isOwner || isCollaborator"
|
|
277
|
+
@click="$router.push({ name: 'music-search' })"
|
|
278
|
+
color="primary"
|
|
279
|
+
size="medium"
|
|
280
|
+
>
|
|
281
|
+
Find Tracks
|
|
282
|
+
</Button>
|
|
283
|
+
</div>
|
|
284
|
+
</section>
|
|
285
|
+
|
|
286
|
+
<!-- More Playlists -->
|
|
287
|
+
<section v-if="!isLoading && playlist && morePlaylists.length" class="more-playlists-section mn-t-big">
|
|
288
|
+
<div class="flex justify-between items-center mn-b-medium">
|
|
289
|
+
<h2 class="h2">More Playlists</h2>
|
|
290
|
+
<router-link
|
|
291
|
+
v-if="playlist.creator"
|
|
292
|
+
:to="getOwnerProfileLink(playlist.creator)"
|
|
293
|
+
class="t-primary hover-opacity"
|
|
294
|
+
>
|
|
295
|
+
See all
|
|
296
|
+
</router-link>
|
|
297
|
+
</div>
|
|
298
|
+
<div class="flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide">
|
|
299
|
+
<li v-for="relatedPlaylist in morePlaylists" :key="relatedPlaylist._id" class="flex-none scroll-snap-align-start">
|
|
300
|
+
<PlaylistCard :playlist="relatedPlaylist" class="w-min-15r transition-cubic-in-out" />
|
|
301
|
+
</li>
|
|
302
|
+
</div>
|
|
303
|
+
</section>
|
|
304
|
+
|
|
305
|
+
<!-- Edit Playlist Modal -->
|
|
306
|
+
<Popup
|
|
307
|
+
:isPopupOpen="showEditModal && (isOwner || isCollaborator)"
|
|
308
|
+
@close-popup="showEditModal = false"
|
|
309
|
+
class="bg-dark pd-medium w-m-30r radius-medium"
|
|
310
|
+
>
|
|
311
|
+
<PlaylistForm
|
|
312
|
+
:editMode="true"
|
|
313
|
+
:url="playlist.url"
|
|
314
|
+
@cancel="showEditModal = false"
|
|
315
|
+
@updated="handlePlaylistUpdated"
|
|
316
|
+
/>
|
|
317
|
+
</Popup>
|
|
318
|
+
|
|
319
|
+
<!-- Delete Confirmation Modal -->
|
|
320
|
+
<Popup
|
|
321
|
+
:isPopupOpen="showDeleteModal"
|
|
322
|
+
@close-popup="showDeleteModal = false"
|
|
323
|
+
class="bg-dark pd-medium w-m-25r radius-medium"
|
|
324
|
+
>
|
|
325
|
+
<h3 class="mn-b-medium">Delete Playlist</h3>
|
|
326
|
+
<p class="t-transp mn-b-medium">Are you sure you want to delete "{{ playlist.title }}"? This action cannot be undone.</p>
|
|
327
|
+
|
|
328
|
+
<div class="flex justify-end gap-small">
|
|
329
|
+
<Button
|
|
330
|
+
@click="showDeleteModal = false"
|
|
331
|
+
color="transp"
|
|
332
|
+
size="medium"
|
|
333
|
+
>
|
|
334
|
+
Cancel
|
|
335
|
+
</Button>
|
|
336
|
+
|
|
337
|
+
<Button
|
|
338
|
+
@click="confirmDelete"
|
|
339
|
+
color="danger"
|
|
340
|
+
size="medium"
|
|
341
|
+
>
|
|
342
|
+
Delete Playlist
|
|
343
|
+
</Button>
|
|
344
|
+
</div>
|
|
345
|
+
</Popup>
|
|
312
346
|
</div>
|
|
313
347
|
</template>
|
|
314
348
|
|
|
315
349
|
<script setup>
|
|
316
|
-
import { ref, computed,
|
|
350
|
+
import { ref, computed, onMounted, watch } from 'vue';
|
|
317
351
|
import { useRoute, useRouter } from 'vue-router';
|
|
318
|
-
import Feed from '@martyrs/src/components/Feed/Feed.vue';
|
|
319
|
-
import TrackCard from '../cards/TrackCard.vue';
|
|
320
352
|
import Button from '@martyrs/src/components/Button/Button.vue';
|
|
321
353
|
import Loader from '@martyrs/src/components/Loader/Loader.vue';
|
|
322
354
|
import Media from '@martyrs/src/components/Media/Media.vue';
|
|
323
355
|
import Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';
|
|
356
|
+
import Feed from '@martyrs/src/components/Feed/Feed.vue';
|
|
324
357
|
import Popup from '@martyrs/src/components/Popup/Popup.vue';
|
|
325
|
-
import Field from '@martyrs/src/components/Field/Field.vue';
|
|
326
|
-
import Radio from '@martyrs/src/components/Radio/Radio.vue';
|
|
327
|
-
import UploadImage from '@martyrs/src/components/UploadImage/UploadImage.vue';
|
|
328
358
|
|
|
329
|
-
//
|
|
359
|
+
// Icons
|
|
330
360
|
import IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';
|
|
331
361
|
import IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';
|
|
332
362
|
import IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';
|
|
333
|
-
import
|
|
363
|
+
import IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';
|
|
364
|
+
import IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';
|
|
365
|
+
import IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';
|
|
366
|
+
import IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';
|
|
367
|
+
import IconRefresh from '@martyrs/src/modules/icons/navigation/IconRefresh.vue';
|
|
368
|
+
import IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';
|
|
334
369
|
|
|
335
|
-
//
|
|
336
|
-
import
|
|
370
|
+
// Components
|
|
371
|
+
import TrackListCard from '../cards/TrackListCard.vue';
|
|
372
|
+
import PlaylistCard from '../cards/PlaylistCard.vue';
|
|
373
|
+
import PlaylistForm from '../forms/PlaylistForm.vue';
|
|
374
|
+
|
|
375
|
+
// Store
|
|
337
376
|
import { state as playlistsState, actions as playlistsActions } from '../../store/playlists.js';
|
|
377
|
+
import { state as tracksState, actions as tracksActions } from '../../store/tracks.js';
|
|
338
378
|
import { actions as playerActions } from '../../store/player.js';
|
|
379
|
+
import { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';
|
|
380
|
+
import * as globals from '@martyrs/src/modules/globals/views/store/globals.js';
|
|
339
381
|
|
|
340
382
|
const route = useRoute();
|
|
341
383
|
const router = useRouter();
|
|
342
384
|
|
|
385
|
+
// Emits
|
|
386
|
+
const emits = defineEmits(['page-loading', 'page-loaded']);
|
|
387
|
+
|
|
343
388
|
// State
|
|
344
|
-
const
|
|
345
|
-
const
|
|
389
|
+
const hasLoaded = ref(false);
|
|
390
|
+
const isFollowing = ref(false);
|
|
346
391
|
const showDropdown = ref(false);
|
|
347
392
|
const showEditModal = ref(false);
|
|
348
393
|
const showDeleteModal = ref(false);
|
|
394
|
+
const followedUsers = ref([]);
|
|
395
|
+
const morePlaylists = ref([]);
|
|
349
396
|
|
|
350
|
-
//
|
|
351
|
-
|
|
397
|
+
// Clear state
|
|
398
|
+
playlistsState.currentPlaylist = null;
|
|
399
|
+
playlistsState.currentPlaylistTracks = [];
|
|
352
400
|
|
|
353
|
-
//
|
|
354
|
-
const editForm = reactive({
|
|
355
|
-
title: '',
|
|
356
|
-
description: '',
|
|
357
|
-
isPublic: true,
|
|
358
|
-
coverUrl: ''
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// Computed properties
|
|
401
|
+
// Computed
|
|
362
402
|
const playlist = computed(() => playlistsState.currentPlaylist);
|
|
363
|
-
const playlistTracks = computed(() => playlistsState.currentPlaylistTracks);
|
|
403
|
+
const playlistTracks = computed(() => playlistsState.currentPlaylistTracks || []);
|
|
364
404
|
|
|
365
405
|
const isOwner = computed(() => {
|
|
366
406
|
if (!playlist.value || !authState.user) return false;
|
|
367
407
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
playlist.value.owner?.target === authState.user._id
|
|
371
|
-
);
|
|
408
|
+
const ownerId = playlist.value.owner?.target?._id || playlist.value.owner?.target;
|
|
409
|
+
return ownerId === authState.user._id;
|
|
372
410
|
});
|
|
373
411
|
|
|
374
412
|
const isCollaborator = computed(() => {
|
|
375
413
|
if (!playlist.value || !authState.user) return false;
|
|
376
414
|
|
|
377
415
|
return playlist.value.collaborators?.some(collab =>
|
|
378
|
-
collab._id
|
|
416
|
+
(collab._id || collab) === authState.user._id
|
|
379
417
|
);
|
|
380
418
|
});
|
|
381
419
|
|
|
382
|
-
|
|
420
|
+
const totalDuration = computed(() => {
|
|
421
|
+
if (!playlistTracks.value.length) return '0:00';
|
|
422
|
+
const totalSeconds = playlistTracks.value.reduce((sum, track) => sum + (track.duration || 0), 0);
|
|
423
|
+
return formatDuration(totalSeconds);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Helper functions
|
|
427
|
+
const getOwnerData = (playlist) => {
|
|
428
|
+
if (!playlist) return null;
|
|
429
|
+
const owner = playlist.creator?.target || playlist.owner?.target;
|
|
430
|
+
return typeof owner === 'object' ? owner : null;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const getOwnerId = (playlist) => {
|
|
434
|
+
if (!playlist) return null;
|
|
435
|
+
const owner = playlist.creator?.target || playlist.owner?.target;
|
|
436
|
+
return typeof owner === 'object' ? owner._id : owner;
|
|
437
|
+
};
|
|
438
|
+
|
|
383
439
|
const getPlaylistOwnerName = (playlist) => {
|
|
384
|
-
if (!playlist
|
|
440
|
+
if (!playlist) return 'Unknown';
|
|
385
441
|
|
|
386
|
-
const owner = playlist
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
return owner.profile.name;
|
|
390
|
-
}
|
|
391
|
-
return owner.name || 'Unknown';
|
|
442
|
+
const owner = getOwnerData(playlist);
|
|
443
|
+
if (owner) {
|
|
444
|
+
return owner.profile?.name || owner.name || 'Unknown';
|
|
392
445
|
}
|
|
393
446
|
|
|
394
447
|
return 'Unknown';
|
|
@@ -397,77 +450,104 @@ const getPlaylistOwnerName = (playlist) => {
|
|
|
397
450
|
const getOwnerProfileLink = (owner) => {
|
|
398
451
|
if (!owner || !owner.target) return { name: 'music-home' };
|
|
399
452
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
return { name: '
|
|
453
|
+
const targetId = typeof owner.target === 'object' ? owner.target._id : owner.target;
|
|
454
|
+
|
|
455
|
+
if (owner.type === 'user' || owner.type === 'User') {
|
|
456
|
+
return { name: 'User Profile', params: { _id: targetId } };
|
|
457
|
+
} else if (owner.type === 'organization' || owner.type === 'Organization') {
|
|
458
|
+
return { name: 'Organizatio', params: { _id: targetId } };
|
|
404
459
|
}
|
|
405
460
|
|
|
406
461
|
return { name: 'music-home' };
|
|
407
462
|
};
|
|
408
463
|
|
|
464
|
+
// Format helpers
|
|
465
|
+
const formatDate = (dateString) => {
|
|
466
|
+
if (!dateString) return 'Unknown';
|
|
467
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
|
468
|
+
year: 'numeric',
|
|
469
|
+
month: 'long',
|
|
470
|
+
day: 'numeric'
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const formatDuration = (seconds) => {
|
|
475
|
+
if (!seconds) return '0:00';
|
|
476
|
+
const h = Math.floor(seconds / 3600);
|
|
477
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
478
|
+
const s = Math.floor(seconds % 60);
|
|
479
|
+
|
|
480
|
+
if (h > 0) {
|
|
481
|
+
return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
|
482
|
+
}
|
|
483
|
+
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
const formatNumber = (num) => {
|
|
487
|
+
if (!num) return '0';
|
|
488
|
+
if (num >= 1000000) {
|
|
489
|
+
return (num / 1000000).toFixed(1) + 'M';
|
|
490
|
+
} else if (num >= 1000) {
|
|
491
|
+
return (num / 1000).toFixed(1) + 'K';
|
|
492
|
+
}
|
|
493
|
+
return num.toString();
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// Actions
|
|
409
497
|
const playPlaylist = () => {
|
|
410
498
|
if (playlistTracks.value && playlistTracks.value.length > 0) {
|
|
411
499
|
playerActions.setQueue(playlistTracks.value);
|
|
412
500
|
}
|
|
413
501
|
};
|
|
414
502
|
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
503
|
+
const shufflePlay = () => {
|
|
504
|
+
if (playlistTracks.value && playlistTracks.value.length > 0) {
|
|
505
|
+
const shuffled = [...playlistTracks.value].sort(() => Math.random() - 0.5);
|
|
506
|
+
playerActions.setQueue(shuffled);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const toggleFollow = async () => {
|
|
511
|
+
isFollowing.value = !isFollowing.value;
|
|
512
|
+
// TODO: Implement actual following
|
|
513
|
+
try {
|
|
514
|
+
if (isFollowing.value) {
|
|
515
|
+
await playlistsActions.followPlaylist(playlist.value._id);
|
|
516
|
+
} else {
|
|
517
|
+
await playlistsActions.unfollowPlaylist(playlist.value._id);
|
|
518
|
+
}
|
|
519
|
+
} catch (error) {
|
|
520
|
+
console.error('Error toggling follow:', error);
|
|
521
|
+
isFollowing.value = !isFollowing.value; // Revert on error
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const toggleFollowUser = (userId) => {
|
|
526
|
+
const index = followedUsers.value.indexOf(userId);
|
|
527
|
+
if (index > -1) {
|
|
528
|
+
followedUsers.value.splice(index, 1);
|
|
529
|
+
} else {
|
|
530
|
+
followedUsers.value.push(userId);
|
|
531
|
+
}
|
|
532
|
+
// TODO: Implement actual following
|
|
418
533
|
};
|
|
419
534
|
|
|
420
535
|
const addToQueue = () => {
|
|
421
536
|
if (playlistTracks.value && playlistTracks.value.length > 0) {
|
|
422
|
-
// Add all tracks to queue
|
|
423
537
|
playlistTracks.value.forEach(track => {
|
|
424
538
|
playerActions.addToQueue(track);
|
|
425
539
|
});
|
|
426
|
-
|
|
427
540
|
showDropdown.value = false;
|
|
428
541
|
}
|
|
429
542
|
};
|
|
430
543
|
|
|
431
544
|
const editPlaylist = () => {
|
|
432
|
-
// Populate edit form with current playlist data
|
|
433
|
-
editForm.title = playlist.value.title;
|
|
434
|
-
editForm.description = playlist.value.description || '';
|
|
435
|
-
editForm.isPublic = playlist.value.isPublic;
|
|
436
|
-
editForm.coverUrl = playlist.value.coverUrl || '';
|
|
437
|
-
|
|
438
545
|
showEditModal.value = true;
|
|
439
546
|
showDropdown.value = false;
|
|
440
547
|
};
|
|
441
548
|
|
|
442
|
-
const updatePlaylist = async () => {
|
|
443
|
-
// Validate form
|
|
444
|
-
validationErrors.title = !editForm.title ? { message: 'Playlist name is required' } : null;
|
|
445
|
-
|
|
446
|
-
// If there are validation errors, don't submit
|
|
447
|
-
if (Object.values(validationErrors).some(error => error !== null)) {
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
try {
|
|
452
|
-
// Update playlist
|
|
453
|
-
const updatedData = {
|
|
454
|
-
_id: playlist.value._id,
|
|
455
|
-
title: editForm.title,
|
|
456
|
-
description: editForm.description,
|
|
457
|
-
isPublic: editForm.isPublic,
|
|
458
|
-
coverUrl: editForm.coverUrl
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
await playlistsActions.updatePlaylist(updatedData);
|
|
462
|
-
showEditModal.value = false;
|
|
463
|
-
} catch (error) {
|
|
464
|
-
console.error('Error updating playlist:', error);
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
|
|
468
549
|
const toggleCollaborative = async () => {
|
|
469
550
|
try {
|
|
470
|
-
// Toggle collaborative status
|
|
471
551
|
const updatedData = {
|
|
472
552
|
_id: playlist.value._id,
|
|
473
553
|
isCollaborative: !playlist.value.isCollaborative
|
|
@@ -491,36 +571,72 @@ const confirmDelete = async () => {
|
|
|
491
571
|
router.push({ name: 'music-library' });
|
|
492
572
|
} catch (error) {
|
|
493
573
|
console.error('Error deleting playlist:', error);
|
|
574
|
+
globals.actions.setError({
|
|
575
|
+
message: 'Failed to delete playlist'
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const removeTrack = async (trackId) => {
|
|
581
|
+
try {
|
|
582
|
+
await playlistsActions.removeTrackFromPlaylist(playlist.value._id, trackId);
|
|
583
|
+
// Refresh playlist data
|
|
584
|
+
await fetchPlaylistData();
|
|
585
|
+
} catch (error) {
|
|
586
|
+
console.error('Error removing track:', error);
|
|
587
|
+
globals.actions.setError({
|
|
588
|
+
message: 'Failed to remove track'
|
|
589
|
+
});
|
|
494
590
|
}
|
|
495
591
|
};
|
|
496
592
|
|
|
497
593
|
const copyLink = () => {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// Could show a success notification here
|
|
501
|
-
showDropdown.value = false;
|
|
502
|
-
});
|
|
594
|
+
navigator.clipboard.writeText(window.location.href);
|
|
595
|
+
showDropdown.value = false;
|
|
503
596
|
};
|
|
504
597
|
|
|
598
|
+
const handlePlaylistUpdated = () => {
|
|
599
|
+
showEditModal.value = false;
|
|
600
|
+
fetchPlaylistData();
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// Data fetching
|
|
505
604
|
const fetchPlaylistData = async () => {
|
|
506
|
-
isLoading.value = true;
|
|
507
|
-
|
|
508
605
|
try {
|
|
509
|
-
// Fetch playlist data
|
|
510
606
|
await playlistsActions.fetchPlaylistByUrl(route.params.url);
|
|
607
|
+
|
|
608
|
+
// Check if following
|
|
609
|
+
if (authState.user && playlist.value) {
|
|
610
|
+
// TODO: Check if user is following this playlist
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Fetch more playlists from the same creator
|
|
614
|
+
if (playlist.value?.creator?.target) {
|
|
615
|
+
const creatorId = typeof playlist.value.creator.target === 'object'
|
|
616
|
+
? playlist.value.creator.target._id
|
|
617
|
+
: playlist.value.creator.target;
|
|
618
|
+
|
|
619
|
+
const playlists = await playlistsActions.fetchPlaylists({
|
|
620
|
+
'creator.target': creatorId,
|
|
621
|
+
isPublic: true,
|
|
622
|
+
limit: 6
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// Filter out current playlist
|
|
626
|
+
morePlaylists.value = playlists.filter(p => p._id !== playlist.value._id).slice(0, 5);
|
|
627
|
+
}
|
|
511
628
|
} catch (error) {
|
|
512
629
|
console.error('Error fetching playlist data:', error);
|
|
513
|
-
} finally {
|
|
514
|
-
isLoading.value = false;
|
|
515
630
|
}
|
|
516
631
|
};
|
|
517
632
|
|
|
518
|
-
//
|
|
519
|
-
onMounted(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
633
|
+
// Lifecycle
|
|
634
|
+
onMounted(async () => {
|
|
635
|
+
emits('page-loading');
|
|
636
|
+
|
|
637
|
+
await fetchPlaylistData();
|
|
638
|
+
|
|
639
|
+
hasLoaded.value = true;
|
|
640
|
+
emits('page-loaded');
|
|
525
641
|
});
|
|
526
642
|
</script>
|