@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.
Files changed (340) hide show
  1. package/dist/_virtual/index.cjs +4 -4
  2. package/dist/_virtual/index.js +4 -4
  3. package/dist/_virtual/index2.cjs +4 -4
  4. package/dist/_virtual/index2.js +4 -4
  5. package/dist/builder.cjs +52 -45
  6. package/dist/builder.js +53 -46
  7. package/dist/{crud-B-kQw3Z5.cjs → crud-JN_LFj01.cjs} +3 -0
  8. package/dist/{crud-Cwx5VlSm.js → crud-sE7GLPbj.js} +3 -0
  9. package/dist/globals.server.cjs +322 -3
  10. package/dist/globals.server.js +303 -1
  11. package/dist/{globals.verifier-D68mHEBl.cjs → globals.verifier-C0zj_LLo.cjs} +8 -1
  12. package/dist/{globals.verifier-CWFz5Gh2.js → globals.verifier-DFqKQ7hK.js} +8 -1
  13. package/dist/inventory.server.cjs +2 -2
  14. package/dist/inventory.server.js +2 -2
  15. package/dist/{main-SZQ1QjeP.js → main-CJm5myDI.js} +631 -607
  16. package/dist/{main-MzmGbSxs.cjs → main-DTaE01lg.cjs} +6 -6
  17. package/dist/martyrs/src/components/Calendar/Calendar.vue2.cjs +1 -1
  18. package/dist/martyrs/src/components/Calendar/Calendar.vue2.cjs.map +1 -1
  19. package/dist/martyrs/src/components/Calendar/Calendar.vue2.js +1 -1
  20. package/dist/martyrs/src/components/Calendar/Calendar.vue2.js.map +1 -1
  21. package/dist/martyrs/src/components/Feed/Feed.vue.cjs +33 -7
  22. package/dist/martyrs/src/components/Feed/Feed.vue.cjs.map +1 -1
  23. package/dist/martyrs/src/components/Feed/Feed.vue.js +33 -7
  24. package/dist/martyrs/src/components/Feed/Feed.vue.js.map +1 -1
  25. package/dist/martyrs/src/components/Field/{Field.vue2.cjs → Field.vue.cjs} +2 -2
  26. package/dist/martyrs/src/components/Field/{Field.vue2.js.map → Field.vue.cjs.map} +1 -1
  27. package/dist/martyrs/src/components/Field/{Field.vue2.js → Field.vue.js} +2 -2
  28. package/dist/martyrs/src/components/Field/Field.vue.js.map +1 -0
  29. package/dist/martyrs/src/components/FieldBig/FieldBig.vue.cjs +1 -1
  30. package/dist/martyrs/src/components/FieldBig/FieldBig.vue.js +1 -1
  31. package/dist/martyrs/src/components/FieldTags/FieldTags.vue.cjs +1 -1
  32. package/dist/martyrs/src/components/FieldTags/FieldTags.vue.js +1 -1
  33. package/dist/martyrs/src/components/Menu/{Menu.vue.cjs → Menu.vue2.cjs} +2 -2
  34. package/dist/martyrs/src/components/Menu/Menu.vue2.cjs.map +1 -0
  35. package/dist/martyrs/src/components/Menu/{Menu.vue.js → Menu.vue2.js} +2 -2
  36. package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +1 -0
  37. package/dist/martyrs/src/components/Menu/MenuItem.vue.js +2 -2
  38. package/dist/martyrs/src/components/Menu/MenuItem.vue.js.map +1 -1
  39. package/dist/martyrs/src/components/PhotoViewer/PhotoViewer.vue.js +4 -4
  40. package/dist/martyrs/src/modules/auth/views/components/pages/EnterCode.vue.cjs +1 -1
  41. package/dist/martyrs/src/modules/auth/views/components/pages/EnterCode.vue.js +1 -1
  42. package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.cjs +1 -1
  43. package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.js +1 -1
  44. package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.cjs +1 -1
  45. package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.js +1 -1
  46. package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.cjs +2 -2
  47. package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +2 -2
  48. package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEditProfile.vue.cjs +1 -1
  49. package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEditProfile.vue.js +1 -1
  50. package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.cjs +1 -1
  51. package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.js +1 -1
  52. package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.cjs +1 -1
  53. package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js +1 -1
  54. package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.cjs +1 -1
  55. package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.js +1 -1
  56. package/dist/martyrs/src/modules/auth/views/components/sections/ProfileEditCredentials.vue.cjs +1 -1
  57. package/dist/martyrs/src/modules/auth/views/components/sections/ProfileEditCredentials.vue.js +1 -1
  58. package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs +2 -2
  59. package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js +2 -2
  60. package/dist/martyrs/src/modules/constructor/components/elements/Card.vue.cjs +1 -1
  61. package/dist/martyrs/src/modules/constructor/components/elements/Card.vue.js +1 -1
  62. package/dist/martyrs/src/modules/constructor/components/elements/Embed.vue.cjs +1 -1
  63. package/dist/martyrs/src/modules/constructor/components/elements/Embed.vue.js +1 -1
  64. package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.cjs +1 -1
  65. package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js +1 -1
  66. package/dist/martyrs/src/modules/events/components/pages/EditEventTickets.vue.cjs +1 -1
  67. package/dist/martyrs/src/modules/events/components/pages/EditEventTickets.vue.js +1 -1
  68. package/dist/martyrs/src/modules/events/components/pages/Event.vue.cjs +1 -1
  69. package/dist/martyrs/src/modules/events/components/pages/Event.vue.js +1 -1
  70. package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.cjs +1 -1
  71. package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +1 -1
  72. package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.cjs +1 -1
  73. package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.js +1 -1
  74. package/dist/martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue.cjs +1 -1
  75. package/dist/martyrs/src/modules/globals/views/components/blocks/BlockSearch.vue.js +1 -1
  76. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs +5 -1
  77. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs.map +1 -1
  78. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js +5 -1
  79. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js.map +1 -1
  80. package/dist/martyrs/src/modules/icons/entities/IconTime.vue.cjs +2 -2
  81. package/dist/martyrs/src/modules/icons/entities/IconTime.vue.js +2 -2
  82. package/dist/martyrs/src/modules/icons/navigation/IconChevronLeft.vue.cjs +2 -2
  83. package/dist/martyrs/src/modules/icons/navigation/IconChevronLeft.vue.js +2 -2
  84. package/dist/martyrs/src/modules/icons/navigation/IconChevronRight.vue.cjs +2 -2
  85. package/dist/martyrs/src/modules/icons/navigation/IconChevronRight.vue.js +2 -2
  86. package/dist/martyrs/src/modules/icons/pages/IconsPage.vue.js +6 -6
  87. package/dist/martyrs/src/modules/icons/pages/IconsPage.vue.js.map +1 -1
  88. package/dist/martyrs/src/modules/inventory/components/forms/AdjustmentForm.vue.cjs +1 -1
  89. package/dist/martyrs/src/modules/inventory/components/forms/AdjustmentForm.vue.js +1 -1
  90. package/dist/martyrs/src/modules/inventory/components/forms/StockAlertsForm.vue.cjs +1 -1
  91. package/dist/martyrs/src/modules/inventory/components/forms/StockAlertsForm.vue.js +1 -1
  92. package/dist/martyrs/src/modules/inventory/components/pages/Inventory.vue.cjs +1 -1
  93. package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.cjs +1 -1
  94. package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.js +1 -1
  95. package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.cjs +22 -15
  96. package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.cjs.map +1 -1
  97. package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js +23 -16
  98. package/dist/martyrs/src/modules/music/components/cards/AlbumCard.vue.js.map +1 -1
  99. package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.cjs +2 -2
  100. package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.cjs.map +1 -1
  101. package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.js +2 -2
  102. package/dist/martyrs/src/modules/music/components/cards/ArtistCard.vue.js.map +1 -1
  103. package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.cjs +31 -13
  104. package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.cjs.map +1 -1
  105. package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.js +33 -15
  106. package/dist/martyrs/src/modules/music/components/cards/PlaylistCard.vue.js.map +1 -1
  107. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs +39 -22
  108. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs.map +1 -1
  109. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js +39 -22
  110. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js.map +1 -1
  111. package/dist/martyrs/src/modules/music/components/forms/AlbumForm.vue.cjs +1 -1
  112. package/dist/martyrs/src/modules/music/components/forms/AlbumForm.vue.js +1 -1
  113. package/dist/martyrs/src/modules/music/components/forms/ArtistForm.vue.cjs +1 -1
  114. package/dist/martyrs/src/modules/music/components/forms/ArtistForm.vue.js +1 -1
  115. package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.cjs +385 -125
  116. package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.cjs.map +1 -1
  117. package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.js +391 -131
  118. package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.js.map +1 -1
  119. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs +24 -7
  120. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs.map +1 -1
  121. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js +25 -8
  122. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js.map +1 -1
  123. package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.cjs +99 -87
  124. package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.cjs.map +1 -1
  125. package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.js +111 -99
  126. package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.js.map +1 -1
  127. package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.cjs +21 -0
  128. package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.cjs.map +1 -0
  129. package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js +21 -0
  130. package/dist/martyrs/src/modules/music/components/layouts/MusicBottomPlayer.vue.js.map +1 -0
  131. package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs +442 -210
  132. package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs.map +1 -1
  133. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +445 -213
  134. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
  135. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs +92 -117
  136. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs.map +1 -1
  137. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js +93 -118
  138. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js.map +1 -1
  139. package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.cjs +72 -113
  140. package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.cjs.map +1 -1
  141. package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js +78 -119
  142. package/dist/martyrs/src/modules/music/components/pages/MusicHome.vue.js.map +1 -1
  143. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs +15 -12
  144. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs.map +1 -1
  145. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.js +15 -12
  146. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.js.map +1 -1
  147. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs +558 -429
  148. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs.map +1 -1
  149. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +560 -431
  150. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
  151. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.cjs +146 -284
  152. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.cjs.map +1 -1
  153. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js +149 -287
  154. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js.map +1 -1
  155. package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs +460 -63
  156. package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs.map +1 -1
  157. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +462 -65
  158. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
  159. package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.cjs +126 -136
  160. package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.cjs.map +1 -1
  161. package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js +129 -139
  162. package/dist/martyrs/src/modules/music/components/player/MusicPlayer.vue.js.map +1 -1
  163. package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.cjs +18 -15
  164. package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.cjs.map +1 -1
  165. package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.js +18 -15
  166. package/dist/martyrs/src/modules/music/components/player/TrackProgress.vue.js.map +1 -1
  167. package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.cjs +28 -23
  168. package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.cjs.map +1 -1
  169. package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js +29 -24
  170. package/dist/martyrs/src/modules/music/components/player/VolumeControl.vue.js.map +1 -1
  171. package/dist/martyrs/src/modules/music/music.client.cjs +3 -6
  172. package/dist/martyrs/src/modules/music/music.client.cjs.map +1 -1
  173. package/dist/martyrs/src/modules/music/music.client.js +9 -12
  174. package/dist/martyrs/src/modules/music/music.client.js.map +1 -1
  175. package/dist/martyrs/src/modules/music/router/music.cjs +27 -1
  176. package/dist/martyrs/src/modules/music/router/music.cjs.map +1 -1
  177. package/dist/martyrs/src/modules/music/router/music.js +27 -1
  178. package/dist/martyrs/src/modules/music/router/music.js.map +1 -1
  179. package/dist/martyrs/src/modules/music/store/artists.cjs +6 -4
  180. package/dist/martyrs/src/modules/music/store/artists.cjs.map +1 -1
  181. package/dist/martyrs/src/modules/music/store/artists.js +6 -4
  182. package/dist/martyrs/src/modules/music/store/artists.js.map +1 -1
  183. package/dist/martyrs/src/modules/music/store/player.cjs +5 -0
  184. package/dist/martyrs/src/modules/music/store/player.cjs.map +1 -1
  185. package/dist/martyrs/src/modules/music/store/player.js +5 -0
  186. package/dist/martyrs/src/modules/music/store/player.js.map +1 -1
  187. package/dist/martyrs/src/modules/music/store/tracks.cjs +22 -0
  188. package/dist/martyrs/src/modules/music/store/tracks.cjs.map +1 -1
  189. package/dist/martyrs/src/modules/music/store/tracks.js +22 -0
  190. package/dist/martyrs/src/modules/music/store/tracks.js.map +1 -1
  191. package/dist/martyrs/src/modules/orders/components/blocks/CardOrderBackoffice.vue.js +2 -2
  192. package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.js +2 -2
  193. package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.cjs +1 -1
  194. package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
  195. package/dist/martyrs/src/modules/orders/components/sections/FormAddCustomer.vue.cjs +1 -1
  196. package/dist/martyrs/src/modules/orders/components/sections/FormAddCustomer.vue.js +1 -1
  197. package/dist/martyrs/src/modules/orders/components/sections/FormCustomerDetails.vue.cjs +1 -1
  198. package/dist/martyrs/src/modules/orders/components/sections/FormCustomerDetails.vue.js +1 -1
  199. package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.cjs +1 -1
  200. package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.js +1 -1
  201. package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.js +2 -2
  202. package/dist/martyrs/src/modules/organizations/components/pages/DepartmentEdit.vue.cjs +1 -1
  203. package/dist/martyrs/src/modules/organizations/components/pages/DepartmentEdit.vue.js +1 -1
  204. package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +1 -1
  205. package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +1 -1
  206. package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs +1 -1
  207. package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +1 -1
  208. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.cjs +1 -1
  209. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
  210. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.cjs +1 -1
  211. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +1 -1
  212. package/dist/martyrs/src/modules/organizations/components/sections/Documents.vue.cjs +1 -1
  213. package/dist/martyrs/src/modules/organizations/components/sections/Documents.vue.js +1 -1
  214. package/dist/martyrs/src/modules/organizations/components/sections/MembersAdd.vue.cjs +1 -1
  215. package/dist/martyrs/src/modules/organizations/components/sections/MembersAdd.vue.js +1 -1
  216. package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.cjs +1 -1
  217. package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.js +1 -1
  218. package/dist/martyrs/src/modules/organizations/router/organizations.cjs +1 -1
  219. package/dist/martyrs/src/modules/organizations/router/organizations.js +1 -1
  220. package/dist/martyrs/src/modules/pages/views/components/blocks/CardPage.vue.cjs +1 -1
  221. package/dist/martyrs/src/modules/pages/views/components/blocks/CardPage.vue.js +1 -1
  222. package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.cjs +1 -1
  223. package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.js +1 -1
  224. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs +1 -1
  225. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +1 -1
  226. package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.cjs +1 -1
  227. package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js +1 -1
  228. package/dist/martyrs/src/modules/products/components/sections/EditAttributes.vue.cjs +1 -1
  229. package/dist/martyrs/src/modules/products/components/sections/EditAttributes.vue.js +1 -1
  230. package/dist/martyrs/src/modules/products/components/sections/EditDiscounts.vue.cjs +1 -1
  231. package/dist/martyrs/src/modules/products/components/sections/EditDiscounts.vue.js +1 -1
  232. package/dist/martyrs/src/modules/products/components/sections/EditVariants.vue.cjs +1 -1
  233. package/dist/martyrs/src/modules/products/components/sections/EditVariants.vue.js +1 -1
  234. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.cjs +4 -3
  235. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.cjs.map +1 -1
  236. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.js +4 -3
  237. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttBar.vue.js.map +1 -1
  238. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.cjs +37 -70
  239. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.cjs.map +1 -1
  240. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.js +38 -71
  241. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttChart.vue.js.map +1 -1
  242. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs +2 -1
  243. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs.map +1 -1
  244. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +6 -5
  245. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js.map +1 -1
  246. package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.cjs +45 -52
  247. package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.cjs.map +1 -1
  248. package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js +46 -53
  249. package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js.map +1 -1
  250. package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.cjs +1 -1
  251. package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.js +1 -1
  252. package/dist/martyrs/src/modules/spots/components/blocks/SpotMemberModify.vue.cjs +1 -1
  253. package/dist/martyrs/src/modules/spots/components/blocks/SpotMemberModify.vue.js +1 -1
  254. package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.cjs +1 -1
  255. package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.js +1 -1
  256. package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.cjs +1 -1
  257. package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.js +1 -1
  258. package/dist/martyrs/src/modules/spots/components/sections/WorktimeEdit.vue.cjs +1 -1
  259. package/dist/martyrs/src/modules/spots/components/sections/WorktimeEdit.vue.js +1 -1
  260. package/dist/martyrs/src/modules/wallet/views/components/blocks/CardDeposit.vue.cjs +1 -1
  261. package/dist/martyrs/src/modules/wallet/views/components/blocks/CardDeposit.vue.js +1 -1
  262. package/dist/martyrs/src/modules/wallet/views/components/blocks/CryptoDeposit.vue.cjs +1 -1
  263. package/dist/martyrs/src/modules/wallet/views/components/blocks/CryptoDeposit.vue.js +1 -1
  264. package/dist/martyrs.cjs.js +1 -1
  265. package/dist/martyrs.css +1 -1
  266. package/dist/martyrs.es.js +1 -1
  267. package/dist/music.server.cjs +124 -31
  268. package/dist/music.server.js +124 -31
  269. package/dist/organizations.server.cjs +1 -1
  270. package/dist/organizations.server.js +1 -1
  271. package/dist/products.server.cjs +2 -2
  272. package/dist/products.server.js +2 -2
  273. package/dist/rents.server.cjs +3 -3
  274. package/dist/rents.server.js +3 -3
  275. package/dist/style.css +373 -80
  276. package/dist/{web-D7lZjuC0.js → web-Dkk0_7TA.js} +1 -1
  277. package/dist/{web-D-YZ9KHz.cjs → web-stVkXd0l.cjs} +1 -1
  278. package/package.json +1 -1
  279. package/src/builder/modes/ssr.prod.js +21 -5
  280. package/src/builder/rspack/rspack.config.ssr.client.js +40 -40
  281. package/src/components/Calendar/Calendar.vue +378 -377
  282. package/src/components/Feed/Feed.vue +28 -2
  283. package/src/modules/globals/controllers/classes/crud/crud.policies.js +5 -0
  284. package/src/modules/globals/controllers/classes/globals.validator.js +8 -1
  285. package/src/modules/globals/views/components/layouts/Client.vue +7 -0
  286. package/src/modules/music/README.md +8 -0
  287. package/src/modules/music/components/SidebarMusic.vue +6 -9
  288. package/src/modules/music/components/cards/AlbumCard.vue +20 -14
  289. package/src/modules/music/components/cards/ArtistCard.vue +1 -1
  290. package/src/modules/music/components/cards/PlaylistCard.vue +31 -11
  291. package/src/modules/music/components/cards/TrackListCard.vue +24 -13
  292. package/src/modules/music/components/forms/PlaylistForm.vue +417 -107
  293. package/src/modules/music/components/forms/SearchForm.vue +31 -8
  294. package/src/modules/music/components/forms/TrackForm.vue +50 -32
  295. package/src/modules/music/components/layouts/MusicBottomPlayer.vue +17 -0
  296. package/src/modules/music/components/pages/Album.vue +373 -186
  297. package/src/modules/music/components/pages/Artist.vue +54 -94
  298. package/src/modules/music/components/pages/MusicHome.vue +59 -56
  299. package/src/modules/music/components/pages/MusicLibrary.vue +13 -11
  300. package/src/modules/music/components/pages/Playlist.vue +495 -379
  301. package/src/modules/music/components/pages/SearchResults.vue +185 -313
  302. package/src/modules/music/components/pages/Track.vue +363 -69
  303. package/src/modules/music/components/player/MusicPlayer.vue +368 -97
  304. package/src/modules/music/components/player/TrackProgress.vue +76 -22
  305. package/src/modules/music/components/player/VolumeControl.vue +61 -28
  306. package/src/modules/music/controllers/search.controller.js +3 -0
  307. package/src/modules/music/controllers/stream.controller.js +11 -3
  308. package/src/modules/music/middlewares/playlists.verifier.js +1 -1
  309. package/src/modules/music/music.client.js +3 -6
  310. package/src/modules/music/music.server.js +8 -4
  311. package/src/modules/music/router/music.js +8 -1
  312. package/src/modules/music/routes/albums.routes.js +37 -5
  313. package/src/modules/music/routes/artists.routes.js +14 -4
  314. package/src/modules/music/routes/genres.routes.js +5 -1
  315. package/src/modules/music/routes/playlists.routes.js +42 -9
  316. package/src/modules/music/routes/tracks.routes.js +27 -2
  317. package/src/modules/music/store/artists.js +6 -2
  318. package/src/modules/music/store/player.js +6 -0
  319. package/src/modules/music/store/tracks.js +31 -0
  320. package/src/modules/music/websocket/streaming.handler.js +7 -1
  321. package/src/modules/rents/controllers/services/rents.services.js +2 -2
  322. package/src/modules/rents/views/components/pages/Gant/GanttBar.vue +4 -3
  323. package/src/modules/rents/views/components/pages/Gant/GanttChart.vue +42 -40
  324. package/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue +3 -1
  325. package/src/modules/rents/views/components/pages/Rents.vue +60 -56
  326. package/dist/globals.websocket-DzvdIBf6.js +0 -306
  327. package/dist/globals.websocket-k6_B1T7k.cjs +0 -322
  328. package/dist/martyrs/src/components/Field/Field.vue2.cjs.map +0 -1
  329. package/dist/martyrs/src/components/Menu/Menu.vue.cjs.map +0 -1
  330. package/dist/martyrs/src/components/Menu/Menu.vue.js.map +0 -1
  331. package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.cjs +0 -69
  332. package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.cjs.map +0 -1
  333. package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.js +0 -69
  334. package/dist/martyrs/src/modules/music/components/cards/TrackCard.vue.js.map +0 -1
  335. package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.cjs +0 -104
  336. package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.cjs.map +0 -1
  337. package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.js +0 -104
  338. package/dist/martyrs/src/modules/music/components/layouts/MusicLayout.vue.js.map +0 -1
  339. package/src/modules/music/components/cards/TrackCard.vue +0 -86
  340. 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
- <div v-if="isLoading" class="w-100 h-25r flex-center flex">
5
- <Loader />
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-grey t-medium">The playlist you're looking for doesn't exist or has been removed.</p>
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
- <div v-else>
14
- <!-- Playlist Header -->
15
- <div class="playlist-header mn-b-medium flex flex-v-center gap-medium">
16
- <div class="playlist-cover">
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
- class="w-15r h-15r object-fit-cover shadow-lg radius-small"
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
- <div class="playlist-info">
24
- <div class="t-small t-uppercase ">Playlist</div>
25
- <h1 class="">{{ playlist.title }}</h1>
26
-
27
- <div v-if="playlist.description" class="playlist-description t-grey mn-t-thin mn-b-small">
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
- <div class="playlist-meta flex flex-v-center">
32
- <router-link
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 Actions -->
49
- <div class="playlist-actions mn-b-medium flex flex-v-center gap-small">
50
- <Button
51
- @click="playPlaylist"
52
- class="play-button bg-main radius-round pd-small flex-v-center flex gap-small hover-scale-1"
53
- :showLoader="false"
54
- :showSucces="false"
55
- >
56
- <IconPlay class="i-small" fill="rgb(var(--black))"/>
57
- <span class="t-black t-medium">Play</span>
58
- </Button>
59
-
60
- <Button
61
- @click="toggleFavorite"
62
- class="bg-transparent border-none pd-zero"
63
- :showLoader="false"
64
- :showSucces="false"
65
- >
66
- <IconLike class="i-medium" :fill="isFavorite ? 'rgb(var(--main))' : 'rgb(var(--white))'"/>
67
- </Button>
68
-
69
- <Dropdown class="pos-relative">
70
- <Button
71
- @click="showDropdown = !showDropdown"
72
- class="bg-transparent border-none pd-zero"
73
- :showLoader="false"
74
- :showSucces="false"
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
- <IconEllipsis class="i-medium" fill="rgb(var(--white))"/>
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
- <template #content>
80
- <ul v-if="showDropdown" class="pd-small bg-dark radius-small pos-absolute pos-t-100 pos-r-0 z-index-3 w-max-15r mn-t-thin">
81
- <li v-if="isOwner" class="mn-b-thin">
82
- <Button
83
- @click="editPlaylist"
84
- class="bg-transparent border-none pd-thin w-100 t-left hover-bg-dark radius-small"
85
- :showLoader="false"
86
- :showSucces="false"
87
- >
88
- <span>Edit Details</span>
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
- </li>
111
- <li v-if="isOwner" class="mn-b-thin">
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
- </li>
121
- <li>
122
- <Button
123
- @click="copyLink"
124
- class="bg-transparent border-none pd-thin w-100 t-left hover-bg-dark radius-small"
125
- :showLoader="false"
126
- :showSucces="false"
127
- >
128
- <span>Copy Link</span>
129
- </Button>
130
- </li>
131
- </ul>
132
- </template>
133
- </Dropdown>
134
- </div>
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
- <Feed
154
- v-else
155
- :store="{
156
- read: () => Promise.resolve(playlistTracks),
157
- state: { isLoading: false }
158
- }"
159
- :external="true"
160
- :items="playlistTracks"
161
- :states="{
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="editForm.coverUrl"
241
- :url="editForm.coverUrl"
242
- class="w-10r h-10r object-fit-cover"
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-10r h-10r flex-center flex">
245
- <IconMusic class="i-big" fill="rgb(var(--grey))"/>
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
- <UploadImage
250
- v-model:photo="editForm.coverUrl"
251
- uploadPath="playlists"
252
- class="flex-1 h-10r bg-dark-transp-25 radius-small"
253
- />
254
- </div>
255
- </div>
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
- @click="showEditModal = false"
260
- type="button"
261
- class="bg-dark-transp-25 pd-small radius-small mn-r-small hover-bg-dark"
262
- :showLoader="false"
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
- Cancel
149
+ {{ followedUsers.includes(getOwnerId(playlist)) ? 'Following' : 'Follow' }}
266
150
  </Button>
267
-
268
- <Button
269
- type="submit"
270
- class="bg-main pd-small radius-small hover-scale-1"
271
- :submit="updatePlaylist"
272
- :showLoader="true"
273
- :showSucces="true"
274
- :validation="!!Object.keys(validationErrors).length"
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
- Save
277
- </Button>
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
- </Popup>
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, reactive, onMounted, watch } from 'vue';
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
- // Import icons
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 IconMusic from '@martyrs/src/modules/icons/entities/IconMusic.vue';
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
- // Import store modules
336
- import { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';
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 isLoading = ref(true);
345
- const isFavorite = ref(false);
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
- // Validation
351
- const validationErrors = reactive({});
397
+ // Clear state
398
+ playlistsState.currentPlaylist = null;
399
+ playlistsState.currentPlaylistTracks = [];
352
400
 
353
- // Edit form
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
- return (
369
- playlist.value.owner?.target?._id === authState.user._id ||
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 === authState.user._id || collab === authState.user._id
416
+ (collab._id || collab) === authState.user._id
379
417
  );
380
418
  });
381
419
 
382
- // Methods
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 || !playlist.owner) return 'Unknown';
440
+ if (!playlist) return 'Unknown';
385
441
 
386
- const owner = playlist.owner.target;
387
- if (typeof owner === 'object') {
388
- if (owner.profile?.name) {
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
- if (owner.type === 'User') {
401
- return { name: 'user-profile', params: { _id: owner.target._id || owner.target } };
402
- } else if (owner.type === 'Organization') {
403
- return { name: 'organization-profile', params: { _id: owner.target._id || owner.target } };
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 toggleFavorite = () => {
416
- isFavorite.value = !isFavorite.value;
417
- // Implement favorite playlist logic here
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
- const url = window.location.href;
499
- navigator.clipboard.writeText(url).then(() => {
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
- // Fetch data when component mounts or URL changes
519
- onMounted(fetchPlaylistData);
520
-
521
- watch(() => route.params.url, (newUrl) => {
522
- if (newUrl) {
523
- fetchPlaylistData();
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>