@ozdao/martyrs 0.2.495 → 0.2.496

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 (116) hide show
  1. package/dist/builder.cjs +1 -1
  2. package/dist/builder.js +1 -1
  3. package/dist/globals.server.cjs +13 -2
  4. package/dist/globals.server.js +13 -2
  5. package/dist/martyrs/src/components/Block/Block.vue.cjs +1 -1
  6. package/dist/martyrs/src/components/Block/Block.vue.js +1 -1
  7. package/dist/martyrs/src/components/Chips/{Chips.vue.cjs → Chips.vue2.cjs} +2 -2
  8. package/dist/martyrs/src/components/Chips/Chips.vue2.cjs.map +1 -0
  9. package/dist/martyrs/src/components/Chips/{Chips.vue.js → Chips.vue2.js} +2 -2
  10. package/dist/martyrs/src/components/Chips/Chips.vue2.js.map +1 -0
  11. package/dist/martyrs/src/components/Dropdown/{Dropdown.vue.cjs → Dropdown.vue2.cjs} +2 -2
  12. package/dist/martyrs/src/components/Dropdown/Dropdown.vue2.cjs.map +1 -0
  13. package/dist/martyrs/src/components/Dropdown/{Dropdown.vue.js → Dropdown.vue2.js} +2 -2
  14. package/dist/martyrs/src/components/Dropdown/{Dropdown.vue.cjs.map → Dropdown.vue2.js.map} +1 -1
  15. package/dist/martyrs/src/components/Feed/Feed.vue.cjs +1 -1
  16. package/dist/martyrs/src/components/Feed/Feed.vue.js +1 -1
  17. package/dist/martyrs/src/components/Menu/{Menu.vue2.cjs → Menu.vue.cjs} +2 -2
  18. package/dist/martyrs/src/components/Menu/Menu.vue.cjs.map +1 -0
  19. package/dist/martyrs/src/components/Menu/{Menu.vue2.js → Menu.vue.js} +2 -2
  20. package/dist/martyrs/src/components/Menu/Menu.vue.js.map +1 -0
  21. package/dist/martyrs/src/components/Tooltip/{Tooltip.vue2.cjs → Tooltip.vue.cjs} +2 -2
  22. package/dist/martyrs/src/components/Tooltip/{Tooltip.vue2.js.map → Tooltip.vue.cjs.map} +1 -1
  23. package/dist/martyrs/src/components/Tooltip/{Tooltip.vue2.js → Tooltip.vue.js} +2 -2
  24. package/dist/martyrs/src/components/Tooltip/Tooltip.vue.js.map +1 -0
  25. package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.cjs +2 -2
  26. package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +2 -2
  27. package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.cjs +1 -1
  28. package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.js +1 -1
  29. package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.cjs +1 -1
  30. package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.js +1 -1
  31. package/dist/martyrs/src/modules/events/components/blocks/CardEvent.vue.cjs +1 -1
  32. package/dist/martyrs/src/modules/events/components/blocks/CardEvent.vue.js +1 -1
  33. package/dist/martyrs/src/modules/events/components/pages/Event.vue.cjs +2 -2
  34. package/dist/martyrs/src/modules/events/components/pages/Event.vue.js +2 -2
  35. package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.cjs +1 -1
  36. package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +1 -1
  37. package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.cjs +2 -2
  38. package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.js +2 -2
  39. package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.cjs +1 -1
  40. package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.js +1 -1
  41. package/dist/martyrs/src/modules/inventory/components/pages/Inventory.vue.cjs +2 -2
  42. package/dist/martyrs/src/modules/inventory/components/pages/Inventory.vue.js +1 -1
  43. package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.cjs +1 -1
  44. package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.js +1 -1
  45. package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.cjs +95 -0
  46. package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.cjs.map +1 -0
  47. package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js +95 -0
  48. package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js.map +1 -0
  49. package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs +86 -139
  50. package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs.map +1 -1
  51. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +96 -149
  52. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
  53. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs +1 -1
  54. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs.map +1 -1
  55. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js +1 -1
  56. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js.map +1 -1
  57. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs +85 -135
  58. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs.map +1 -1
  59. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +101 -151
  60. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
  61. package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs +146 -158
  62. package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs.map +1 -1
  63. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +171 -183
  64. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
  65. package/dist/martyrs/src/modules/music/router/music.cjs +2 -2
  66. package/dist/martyrs/src/modules/music/router/music.js +2 -2
  67. package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.cjs +1 -1
  68. package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.js +1 -1
  69. package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.cjs +1 -1
  70. package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.js +1 -1
  71. package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.cjs +1 -1
  72. package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.js +1 -1
  73. package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.cjs +1 -1
  74. package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.js +1 -1
  75. package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +2 -2
  76. package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +2 -2
  77. package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs +1 -1
  78. package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +1 -1
  79. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.cjs +1 -1
  80. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
  81. package/dist/martyrs/src/modules/organizations/components/sections/DetailsTabSection.vue.cjs +2 -2
  82. package/dist/martyrs/src/modules/organizations/components/sections/DetailsTabSection.vue.js +2 -2
  83. package/dist/martyrs/src/modules/organizations/router/organizations.cjs +1 -1
  84. package/dist/martyrs/src/modules/organizations/router/organizations.js +1 -1
  85. package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.cjs +1 -1
  86. package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.js +1 -1
  87. package/dist/martyrs/src/modules/products/components/blocks/ProductDiscounts.vue.cjs +2 -2
  88. package/dist/martyrs/src/modules/products/components/blocks/ProductDiscounts.vue.js +2 -2
  89. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs +1 -1
  90. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +1 -1
  91. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs +1 -1
  92. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +1 -1
  93. package/dist/martyrs/src/modules/spots/components/blocks/CardSpot.vue.cjs +1 -1
  94. package/dist/martyrs/src/modules/spots/components/blocks/CardSpot.vue.js +1 -1
  95. package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.cjs +1 -1
  96. package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.js +1 -1
  97. package/dist/martyrs/src/modules/wallet/views/components/pages/Wallet.vue.cjs +3 -3
  98. package/dist/martyrs/src/modules/wallet/views/components/pages/Wallet.vue.js +3 -3
  99. package/dist/martyrs.css +1 -1
  100. package/dist/style.css +13 -0
  101. package/package.json +1 -1
  102. package/src/builder/rspack/rspack.config.ssr.client.js +1 -1
  103. package/src/modules/globals/controllers/classes/globals.websocket.js +11 -2
  104. package/src/modules/music/README.md +2 -0
  105. package/src/modules/music/components/cards/ArtistCardSmall.vue +92 -0
  106. package/src/modules/music/components/pages/Album.vue +20 -56
  107. package/src/modules/music/components/pages/Artist.vue +1 -1
  108. package/src/modules/music/components/pages/Playlist.vue +26 -44
  109. package/src/modules/music/components/pages/Track.vue +51 -74
  110. package/src/styles/base/all.scss +1 -0
  111. package/dist/martyrs/src/components/Chips/Chips.vue.cjs.map +0 -1
  112. package/dist/martyrs/src/components/Chips/Chips.vue.js.map +0 -1
  113. package/dist/martyrs/src/components/Dropdown/Dropdown.vue.js.map +0 -1
  114. package/dist/martyrs/src/components/Menu/Menu.vue2.cjs.map +0 -1
  115. package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +0 -1
  116. package/dist/martyrs/src/components/Tooltip/Tooltip.vue2.cjs.map +0 -1
@@ -5,10 +5,11 @@ const vueRouter = require("vue-router");
5
5
  const Button = require("../../../../components/Button/Button.vue.cjs");
6
6
  const Loader = require("../../../../components/Loader/Loader.vue2.cjs");
7
7
  const Media = require("../../../../components/Media/Media.vue.cjs");
8
- const Dropdown = require("../../../../components/Dropdown/Dropdown.vue.cjs");
8
+ const Dropdown = require("../../../../components/Dropdown/Dropdown.vue2.cjs");
9
9
  const Popup = require("../../../../components/Popup/Popup.vue.cjs");
10
10
  const Feed = require("../../../../components/Feed/Feed.vue.cjs");
11
11
  const IconPlay = require("../../../icons/navigation/IconPlay.vue.cjs");
12
+ const IconPause = require("../../../icons/navigation/IconPause.vue.cjs");
12
13
  const IconLike = require("../../../icons/navigation/IconLike.vue.cjs");
13
14
  const IconEllipsis = require("../../../icons/navigation/IconEllipsis.vue.cjs");
14
15
  const IconAdd = require("../../../icons/navigation/IconAdd.vue.cjs");
@@ -17,6 +18,7 @@ const IconCalendar = require("../../../icons/entities/IconCalendar.vue.cjs");
17
18
  const IconCheckmark = require("../../../icons/navigation/IconCheckmark.vue.cjs");
18
19
  const IconShow = require("../../../icons/actions/IconShow.vue.cjs");
19
20
  const TrackListCard = require("../cards/TrackListCard.vue.cjs");
21
+ const ArtistCardSmall = require("../cards/ArtistCardSmall.vue.cjs");
20
22
  const tracks = require("../../store/tracks.cjs");
21
23
  const player = require("../../store/player.cjs");
22
24
  const auth = require("../../../auth/views/store/auth.cjs");
@@ -34,71 +36,66 @@ const _hoisted_4 = {
34
36
  class: "track-content cols-2-fit-content mobile:cols-1 gap-big"
35
37
  };
36
38
  const _hoisted_5 = { class: "pos-sticky pos-t-0 mobile:pos-relative track-cover-section" };
37
- const _hoisted_6 = { class: "cover-container relative mn-b-medium radius-big overflow-hidden shadow-big" };
38
- const _hoisted_7 = { class: "stats-grid grid cols-2 gap-small" };
39
- const _hoisted_8 = { class: "stat-card bg-light pd-medium radius-medium t-center" };
40
- const _hoisted_9 = { class: "mn-b-thin" };
41
- const _hoisted_10 = { class: "stat-card bg-light pd-medium radius-medium t-center" };
42
- const _hoisted_11 = { class: "mn-b-thin" };
43
- const _hoisted_12 = { class: "track-details-section" };
44
- const _hoisted_13 = { class: "flex items-center gap-small mn-b-small" };
45
- const _hoisted_14 = {
39
+ const _hoisted_6 = { class: "cover-container pos-relative mn-b-medium radius-big o-hidden" };
40
+ const _hoisted_7 = { class: "cover-overlay w-100 h-100 pos-absolute pos-t-0 pos-r-0 bg-black-transp-40 flex flex-center" };
41
+ const _hoisted_8 = { class: "stats-grid grid cols-2 gap-small" };
42
+ const _hoisted_9 = { class: "stat-card bg-light pd-medium radius-medium t-center" };
43
+ const _hoisted_10 = { class: "mn-b-thin" };
44
+ const _hoisted_11 = { class: "stat-card bg-light pd-medium radius-medium t-center" };
45
+ const _hoisted_12 = { class: "mn-b-thin" };
46
+ const _hoisted_13 = { class: "track-details-section" };
47
+ const _hoisted_14 = { class: "h1 mn-b-medium" };
48
+ const _hoisted_15 = { class: "flex gap-small mn-b-medium" };
49
+ const _hoisted_16 = { class: "dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin" };
50
+ const _hoisted_17 = { class: "artists-section mn-b-medium" };
51
+ const _hoisted_18 = {
46
52
  key: 0,
47
- class: "badge bg-danger-transp-20 t-danger pd-thin-big radius-small t-small"
53
+ class: "t-medium mn-b-small"
48
54
  };
49
- const _hoisted_15 = { class: "h1 mn-b-medium" };
50
- const _hoisted_16 = { class: "flex gap-small mn-b-medium" };
51
- const _hoisted_17 = { class: "dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin" };
52
- const _hoisted_18 = { class: "artist-card bg-light pd-medium radius-medium flex items-center gap-medium mn-b-big" };
53
- const _hoisted_19 = { class: "artist-avatar" };
54
- const _hoisted_20 = {
55
- key: 1,
56
- class: "w-4r h-4r radius-full bg-primary flex-center"
57
- };
58
- const _hoisted_21 = { class: "flex items-center gap-thin" };
59
- const _hoisted_22 = { class: "t-large" };
60
- const _hoisted_23 = { class: "metadata-grid grid cols-2 gap-small mn-b-big" };
61
- const _hoisted_24 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
62
- const _hoisted_25 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
55
+ const _hoisted_19 = { class: "flex flex-col gap-small" };
56
+ const _hoisted_20 = { class: "metadata-grid grid cols-2 gap-small mn-b-medium" };
57
+ const _hoisted_21 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
58
+ const _hoisted_22 = { class: "t-medium" };
59
+ const _hoisted_23 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
60
+ const _hoisted_24 = { class: "t-medium" };
61
+ const _hoisted_25 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
63
62
  const _hoisted_26 = { class: "t-medium" };
64
63
  const _hoisted_27 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
65
- const _hoisted_28 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
66
- const _hoisted_29 = { class: "t-medium" };
67
- const _hoisted_30 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
68
- const _hoisted_31 = { class: "icon-wrapper bg-success-transp-20 w-3r h-3r radius-small flex-center" };
69
- const _hoisted_32 = { class: "t-medium t-success" };
70
- const _hoisted_33 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
71
- const _hoisted_34 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
72
- const _hoisted_35 = { class: "t-medium" };
73
- const _hoisted_36 = {
64
+ const _hoisted_28 = { class: "t-medium" };
65
+ const _hoisted_29 = {
74
66
  key: 0,
75
- class: "album-card bg-light pd-medium radius-medium mn-b-medium"
67
+ class: "t-medium mn-b-small"
76
68
  };
77
- const _hoisted_37 = { class: "t-medium" };
78
- const _hoisted_38 = {
69
+ const _hoisted_30 = {
79
70
  key: 1,
80
- class: "tags-section mn-b-medium"
71
+ class: "album-card bg-light pd-medium radius-medium mn-b-medium"
81
72
  };
82
- const _hoisted_39 = { class: "flex gap-thin flex-wrap" };
83
- const _hoisted_40 = {
73
+ const _hoisted_31 = { class: "t-medium" };
74
+ const _hoisted_32 = {
84
75
  key: 2,
85
- class: "description-section bg-light pd-medium radius-medium mn-b-medium"
76
+ class: "tags-section mn-b-medium"
86
77
  };
87
- const _hoisted_41 = { class: "t-transp" };
88
- const _hoisted_42 = {
78
+ const _hoisted_33 = { class: "flex gap-thin flex-wrap" };
79
+ const _hoisted_34 = {
89
80
  key: 3,
81
+ class: "description-section bg-light pd-medium radius-medium mn-b-medium"
82
+ };
83
+ const _hoisted_35 = { class: "t-transp" };
84
+ const _hoisted_36 = {
85
+ key: 4,
90
86
  class: "lyrics-section bg-light pd-medium radius-medium"
91
87
  };
92
- const _hoisted_43 = { class: "t-transp t-small" };
93
- const _hoisted_44 = {
88
+ const _hoisted_37 = { class: "t-transp t-small" };
89
+ const _hoisted_38 = {
94
90
  key: 3,
95
91
  class: "related-section mn-t-big"
96
92
  };
97
- const _hoisted_45 = { class: "bg-light radius-medium o-hidden" };
93
+ const _hoisted_39 = { class: "bg-light radius-medium o-hidden" };
98
94
  const _sfc_main = {
99
95
  __name: "Track",
100
96
  emits: ["page-loading", "page-loaded"],
101
97
  setup(__props, { emit: __emit }) {
98
+ const isPlaying = vue.computed(() => player.state.isPlaying);
102
99
  const route = vueRouter.useRoute();
103
100
  const router = vueRouter.useRouter();
104
101
  const emits = __emit;
@@ -205,26 +202,47 @@ const _sfc_main = {
205
202
  url: track.value.coverUrl || track.value.album && track.value.album.coverUrl || "/logo/logo-placeholder.jpg",
206
203
  alt: track.value.title,
207
204
  class: "aspect-1x1 w-100 radius-medium o-hidden"
208
- }, null, 8, ["url", "alt"])
205
+ }, null, 8, ["url", "alt"]),
206
+ vue.createElementVNode("div", _hoisted_7, [
207
+ vue.createVNode(Button.default, {
208
+ onClick: playTrack,
209
+ color: "white",
210
+ size: "big",
211
+ class: "w-5r h-5r radius-big bg-main shadow-big hover-scale-110"
212
+ }, {
213
+ default: vue.withCtx(() => [
214
+ !isPlaying.value ? (vue.openBlock(), vue.createBlock(IconPlay.default, {
215
+ key: 0,
216
+ fill: "rgb(var(--white))",
217
+ class: "i-medium"
218
+ })) : (vue.openBlock(), vue.createBlock(IconPause.default, {
219
+ key: 1,
220
+ fill: "rgb(var(--white))",
221
+ class: "i-medium"
222
+ }))
223
+ ]),
224
+ _: 1
225
+ })
226
+ ])
209
227
  ]),
210
- vue.createElementVNode("div", _hoisted_7, [
211
- vue.createElementVNode("div", _hoisted_8, [
212
- vue.createElementVNode("div", _hoisted_9, vue.toDisplayString(formatNumber(track.value.playCount)), 1),
228
+ vue.createElementVNode("div", _hoisted_8, [
229
+ vue.createElementVNode("div", _hoisted_9, [
230
+ vue.createElementVNode("div", _hoisted_10, vue.toDisplayString(formatNumber(track.value.playCount)), 1),
213
231
  _cache[4] || (_cache[4] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Plays", -1))
214
232
  ]),
215
- vue.createElementVNode("div", _hoisted_10, [
216
- vue.createElementVNode("div", _hoisted_11, vue.toDisplayString(formatNumber(track.value.views)), 1),
233
+ vue.createElementVNode("div", _hoisted_11, [
234
+ vue.createElementVNode("div", _hoisted_12, vue.toDisplayString(formatNumber(track.value.views)), 1),
217
235
  _cache[5] || (_cache[5] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Views", -1))
218
236
  ])
219
237
  ])
220
238
  ]),
221
- vue.createElementVNode("div", _hoisted_12, [
222
- vue.createElementVNode("div", _hoisted_13, [
223
- _cache[6] || (_cache[6] = vue.createElementVNode("span", { class: "badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase" }, "Single", -1)),
224
- track.value.isExplicit ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_14, "Explicit")) : vue.createCommentVNode("", true)
225
- ]),
226
- vue.createElementVNode("h1", _hoisted_15, vue.toDisplayString(track.value.title), 1),
227
- vue.createElementVNode("div", _hoisted_16, [
239
+ vue.createElementVNode("div", _hoisted_13, [
240
+ _cache[19] || (_cache[19] = vue.createElementVNode("div", { class: "flex items-center gap-small mn-b-small" }, [
241
+ vue.createElementVNode("span", { class: "bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase" }, "Single"),
242
+ vue.createElementVNode("span", { class: "bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase" }, "Explicit")
243
+ ], -1)),
244
+ vue.createElementVNode("h1", _hoisted_14, vue.toDisplayString(track.value.title), 1),
245
+ vue.createElementVNode("div", _hoisted_15, [
228
246
  vue.createVNode(Button.default, {
229
247
  onClick: playTrack,
230
248
  color: "primary",
@@ -232,11 +250,16 @@ const _sfc_main = {
232
250
  class: "flex-1 t-white bg-black radius-thin flex-center gap-thin"
233
251
  }, {
234
252
  default: vue.withCtx(() => [
235
- vue.createVNode(IconPlay.default, {
253
+ !isPlaying.value ? (vue.openBlock(), vue.createBlock(IconPlay.default, {
254
+ key: 0,
236
255
  fill: "rgb(var(--white))",
237
256
  class: "i-medium"
238
- }),
239
- _cache[7] || (_cache[7] = vue.createTextVNode(" Play "))
257
+ })) : (vue.openBlock(), vue.createBlock(IconPause.default, {
258
+ key: 1,
259
+ fill: "rgb(var(--white))",
260
+ class: "i-medium"
261
+ })),
262
+ vue.createTextVNode(" " + vue.toDisplayString(!isPlaying.value ? "Play" : "Pause"), 1)
240
263
  ]),
241
264
  _: 1
242
265
  }),
@@ -248,7 +271,7 @@ const _sfc_main = {
248
271
  }, {
249
272
  default: vue.withCtx(() => [
250
273
  vue.createVNode(IconAdd.default, { class: "i-medium" }),
251
- _cache[8] || (_cache[8] = vue.createTextVNode(" Add to Queue "))
274
+ _cache[6] || (_cache[6] = vue.createTextVNode(" Add to Queue "))
252
275
  ]),
253
276
  _: 1
254
277
  }),
@@ -261,7 +284,7 @@ const _sfc_main = {
261
284
  default: vue.withCtx(() => [
262
285
  vue.createVNode(IconLike.default, {
263
286
  class: "i-medium",
264
- fill: isFavorite.value
287
+ fill: isFavorite.value ? "rgb(var(--main)" : "rgb(var(--black)"
265
288
  }, null, 8, ["fill"]),
266
289
  vue.createTextVNode(" " + vue.toDisplayString(isFavorite.value ? "Liked" : "Like"), 1)
267
290
  ]),
@@ -286,14 +309,14 @@ const _sfc_main = {
286
309
  })
287
310
  ]),
288
311
  default: vue.withCtx(() => [
289
- vue.createElementVNode("div", _hoisted_17, [
312
+ vue.createElementVNode("div", _hoisted_16, [
290
313
  vue.createVNode(Button.default, {
291
314
  onClick: _cache[0] || (_cache[0] = ($event) => showAddToPlaylistModal.value = true),
292
315
  color: "transp",
293
316
  size: "small",
294
317
  class: "w-100 justify-start"
295
318
  }, {
296
- default: vue.withCtx(() => _cache[9] || (_cache[9] = [
319
+ default: vue.withCtx(() => _cache[7] || (_cache[7] = [
297
320
  vue.createTextVNode(" Add to Playlist ")
298
321
  ])),
299
322
  _: 1
@@ -304,20 +327,20 @@ const _sfc_main = {
304
327
  size: "small",
305
328
  class: "w-100 justify-start"
306
329
  }, {
307
- default: vue.withCtx(() => _cache[10] || (_cache[10] = [
330
+ default: vue.withCtx(() => _cache[8] || (_cache[8] = [
308
331
  vue.createTextVNode(" Copy Link ")
309
332
  ])),
310
333
  _: 1
311
334
  }),
312
335
  isOwner.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
313
- _cache[13] || (_cache[13] = vue.createElementVNode("hr", { class: "mn-v-thin border-dark-transp-10" }, null, -1)),
336
+ _cache[11] || (_cache[11] = vue.createElementVNode("hr", { class: "mn-v-thin border-dark-transp-10" }, null, -1)),
314
337
  vue.createVNode(Button.default, {
315
338
  onClick: editTrack,
316
339
  color: "transp",
317
340
  size: "small",
318
341
  class: "w-100 justify-start"
319
342
  }, {
320
- default: vue.withCtx(() => _cache[11] || (_cache[11] = [
343
+ default: vue.withCtx(() => _cache[9] || (_cache[9] = [
321
344
  vue.createTextVNode(" Edit Track ")
322
345
  ])),
323
346
  _: 1
@@ -328,7 +351,7 @@ const _sfc_main = {
328
351
  size: "small",
329
352
  class: "w-100 justify-start"
330
353
  }, {
331
- default: vue.withCtx(() => _cache[12] || (_cache[12] = [
354
+ default: vue.withCtx(() => _cache[10] || (_cache[10] = [
332
355
  vue.createTextVNode(" Delete Track ")
333
356
  ])),
334
357
  _: 1
@@ -339,130 +362,95 @@ const _sfc_main = {
339
362
  _: 1
340
363
  }, 8, ["label", "modelValue"])
341
364
  ]),
342
- vue.createElementVNode("div", _hoisted_18, [
343
- vue.createVNode(_component_router_link, {
344
- to: { name: "artist", params: { url: track.value.artist.url } },
345
- class: "flex items-center gap-medium flex-1 hover-opacity"
346
- }, {
347
- default: vue.withCtx(() => [
348
- vue.createElementVNode("div", _hoisted_19, [
349
- track.value.artist.photoUrl ? (vue.openBlock(), vue.createBlock(Media.default, {
350
- key: 0,
351
- src: track.value.artist.photoUrl,
352
- alt: track.value.artist.name,
353
- class: "w-4r h-4r radius-full object-cover"
354
- }, null, 8, ["src", "alt"])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_20, vue.toDisplayString(track.value.artist.name.charAt(0)), 1))
355
- ]),
356
- vue.createElementVNode("div", null, [
357
- vue.createElementVNode("div", _hoisted_21, [
358
- vue.createElementVNode("span", _hoisted_22, vue.toDisplayString(track.value.artist.name), 1),
359
- track.value.artist.isVerified ? (vue.openBlock(), vue.createBlock(IconCheckmark.default, {
360
- key: 0,
361
- class: "w-1r h-1r t-primary"
362
- })) : vue.createCommentVNode("", true)
363
- ]),
364
- _cache[14] || (_cache[14] = vue.createElementVNode("span", { class: "t-small t-transp" }, "Artist", -1))
365
- ])
366
- ]),
367
- _: 1
368
- }, 8, ["to"]),
369
- !isOwner.value ? (vue.openBlock(), vue.createBlock(Button.default, {
370
- key: 0,
371
- onClick: toggleFollowArtist,
372
- color: isFollowingArtist.value ? "primary" : "transp",
373
- size: "small"
374
- }, {
375
- default: vue.withCtx(() => [
376
- vue.createTextVNode(vue.toDisplayString(isFollowingArtist.value ? "Following" : "Follow"), 1)
377
- ]),
378
- _: 1
379
- }, 8, ["color"])) : vue.createCommentVNode("", true)
365
+ vue.createElementVNode("div", _hoisted_17, [
366
+ track.value.artist ? (vue.openBlock(), vue.createElementBlock("h3", _hoisted_18, "Artist")) : vue.createCommentVNode("", true),
367
+ vue.createElementVNode("div", _hoisted_19, [
368
+ (vue.openBlock(), vue.createBlock(ArtistCardSmall.default, {
369
+ key: track.value.artist._id,
370
+ artist: track.value.artist,
371
+ "is-following": isFollowingArtist.value,
372
+ "show-follow-button": !isOwner.value,
373
+ onToggleFollow: toggleFollowArtist
374
+ }, null, 8, ["artist", "is-following", "show-follow-button"]))
375
+ ])
380
376
  ]),
381
- vue.createElementVNode("div", _hoisted_23, [
382
- vue.createElementVNode("div", _hoisted_24, [
383
- vue.createElementVNode("div", _hoisted_25, [
384
- vue.createVNode(IconTime.default, { class: "i-regular t-primary" })
385
- ]),
377
+ _cache[20] || (_cache[20] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Metadata", -1)),
378
+ vue.createElementVNode("div", _hoisted_20, [
379
+ vue.createElementVNode("div", _hoisted_21, [
380
+ vue.createVNode(IconCalendar.default, { class: "i-regular t-primary" }),
386
381
  vue.createElementVNode("div", null, [
387
- _cache[15] || (_cache[15] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Duration", -1)),
388
- vue.createElementVNode("div", _hoisted_26, vue.toDisplayString(formatDuration(track.value.duration)), 1)
382
+ _cache[12] || (_cache[12] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Released", -1)),
383
+ vue.createElementVNode("div", _hoisted_22, vue.toDisplayString(formatDate(track.value.releaseDate)), 1)
389
384
  ])
390
385
  ]),
391
- vue.createElementVNode("div", _hoisted_27, [
392
- vue.createElementVNode("div", _hoisted_28, [
393
- vue.createVNode(IconCalendar.default, { class: "i-regular t-primary" })
394
- ]),
386
+ vue.createElementVNode("div", _hoisted_23, [
387
+ vue.createVNode(IconTime.default, { class: "i-regular t-primary" }),
395
388
  vue.createElementVNode("div", null, [
396
- _cache[16] || (_cache[16] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Released", -1)),
397
- vue.createElementVNode("div", _hoisted_29, vue.toDisplayString(formatDate(track.value.releaseDate)), 1)
389
+ _cache[13] || (_cache[13] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Duration", -1)),
390
+ vue.createElementVNode("div", _hoisted_24, vue.toDisplayString(formatDuration(track.value.duration)), 1)
398
391
  ])
399
392
  ]),
400
- vue.createElementVNode("div", _hoisted_30, [
401
- vue.createElementVNode("div", _hoisted_31, [
402
- vue.createVNode(IconCheckmark.default, { class: "i-regular t-success" })
403
- ]),
393
+ vue.createElementVNode("div", _hoisted_25, [
394
+ vue.createVNode(IconCheckmark.default, { class: "i-regular t-primary" }),
404
395
  vue.createElementVNode("div", null, [
405
- _cache[17] || (_cache[17] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Status", -1)),
406
- vue.createElementVNode("div", _hoisted_32, vue.toDisplayString(track.value.status), 1)
396
+ _cache[14] || (_cache[14] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Status", -1)),
397
+ vue.createElementVNode("div", _hoisted_26, vue.toDisplayString(track.value.status), 1)
407
398
  ])
408
399
  ]),
409
- vue.createElementVNode("div", _hoisted_33, [
410
- vue.createElementVNode("div", _hoisted_34, [
411
- vue.createVNode(IconShow.default, { class: "i-regular t-primary" })
412
- ]),
400
+ vue.createElementVNode("div", _hoisted_27, [
401
+ vue.createVNode(IconShow.default, { class: "i-regular t-primary" }),
413
402
  vue.createElementVNode("div", null, [
414
- _cache[18] || (_cache[18] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Visibility", -1)),
415
- vue.createElementVNode("div", _hoisted_35, vue.toDisplayString(track.value.isPublic ? "Public" : "Private"), 1)
403
+ _cache[15] || (_cache[15] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Visibility", -1)),
404
+ vue.createElementVNode("div", _hoisted_28, vue.toDisplayString(track.value.isPublic ? "Public" : "Private"), 1)
416
405
  ])
417
406
  ])
418
407
  ]),
419
- track.value.album ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_36, [
420
- _cache[19] || (_cache[19] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase mn-b-thin" }, "From Album", -1)),
408
+ track.value.album ? (vue.openBlock(), vue.createElementBlock("h3", _hoisted_29, "From Album")) : vue.createCommentVNode("", true),
409
+ track.value.album ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_30, [
421
410
  vue.createVNode(_component_router_link, {
422
411
  to: `/album/${track.value.album.url}`,
423
- class: "flex items-center gap-medium hover-opacity"
412
+ class: "flex flex-v-center gap-thin hover-opacity"
424
413
  }, {
425
414
  default: vue.withCtx(() => [
426
- track.value.album.coverUrl ? (vue.openBlock(), vue.createBlock(Media.default, {
427
- key: 0,
428
- src: track.value.album.coverUrl,
415
+ vue.createVNode(Media.default, {
416
+ url: track.value.album.coverArt || "/logo/logo-placeholder.jpg",
429
417
  alt: track.value.album.title,
430
- class: "w-3r h-3r radius-small object-cover"
431
- }, null, 8, ["src", "alt"])) : vue.createCommentVNode("", true),
432
- vue.createElementVNode("span", _hoisted_37, vue.toDisplayString(track.value.album.title), 1)
418
+ class: "w-3r h-3r radius-thin o-hidden object-cover"
419
+ }, null, 8, ["url", "alt"]),
420
+ vue.createElementVNode("span", _hoisted_31, vue.toDisplayString(track.value.album.title), 1)
433
421
  ]),
434
422
  _: 1
435
423
  }, 8, ["to"])
436
424
  ])) : vue.createCommentVNode("", true),
437
- track.value.genre && track.value.genre.length || track.value.tags && track.value.tags.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_38, [
438
- _cache[20] || (_cache[20] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Genres & Tags", -1)),
439
- vue.createElementVNode("div", _hoisted_39, [
425
+ track.value.genre && track.value.genre.length || track.value.tags && track.value.tags.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_32, [
426
+ _cache[16] || (_cache[16] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Genres & Tags", -1)),
427
+ vue.createElementVNode("div", _hoisted_33, [
440
428
  (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(track.value.genre, (genre) => {
441
429
  return vue.openBlock(), vue.createElementBlock("span", {
442
430
  key: genre,
443
- class: "tag bg-primary-transp-20 t-primary pd-thin-big radius-small t-small hover-bg-primary-transp-30 cursor-pointer"
444
- }, vue.toDisplayString(genre), 1);
431
+ class: "tag bg-main t-medium pd-thin radius-thin t-small cursor-pointer"
432
+ }, vue.toDisplayString(genre.name || genre), 1);
445
433
  }), 128)),
446
434
  (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(track.value.tags, (tag) => {
447
435
  return vue.openBlock(), vue.createElementBlock("span", {
448
436
  key: tag,
449
437
  class: "tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer"
450
- }, " #" + vue.toDisplayString(tag), 1);
438
+ }, " #" + vue.toDisplayString(tag.name || tag), 1);
451
439
  }), 128))
452
440
  ])
453
441
  ])) : vue.createCommentVNode("", true),
454
- track.value.description ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_40, [
455
- _cache[21] || (_cache[21] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "About", -1)),
456
- vue.createElementVNode("p", _hoisted_41, vue.toDisplayString(track.value.description), 1)
442
+ track.value.description ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_34, [
443
+ _cache[17] || (_cache[17] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "About", -1)),
444
+ vue.createElementVNode("p", _hoisted_35, vue.toDisplayString(track.value.description), 1)
457
445
  ])) : vue.createCommentVNode("", true),
458
- track.value.lyrics ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_42, [
459
- _cache[22] || (_cache[22] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Lyrics", -1)),
460
- vue.createElementVNode("pre", _hoisted_43, vue.toDisplayString(track.value.lyrics), 1)
446
+ track.value.lyrics ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_36, [
447
+ _cache[18] || (_cache[18] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Lyrics", -1)),
448
+ vue.createElementVNode("pre", _hoisted_37, vue.toDisplayString(track.value.lyrics), 1)
461
449
  ])) : vue.createCommentVNode("", true)
462
450
  ])
463
451
  ])) : vue.createCommentVNode("", true),
464
- track.value && relatedTracks.value && relatedTracks.value.length ? (vue.openBlock(), vue.createElementBlock("section", _hoisted_44, [
465
- _cache[23] || (_cache[23] = vue.createElementVNode("h2", { class: "h2 mn-b-medium" }, "Related Tracks", -1)),
452
+ track.value && relatedTracks.value && relatedTracks.value.length ? (vue.openBlock(), vue.createElementBlock("section", _hoisted_38, [
453
+ _cache[21] || (_cache[21] = vue.createElementVNode("h2", { class: "h2 mn-b-medium" }, "Related Tracks", -1)),
466
454
  vue.createVNode(Feed.default, {
467
455
  store: {
468
456
  read: () => new _ctx.Promise((resolve) => resolve(relatedTracks.value || [])),
@@ -480,7 +468,7 @@ const _sfc_main = {
480
468
  class: "grid cols-2 cols-m-3 cols-l-4 gap-medium"
481
469
  }, {
482
470
  default: vue.withCtx(({ items }) => [
483
- vue.createElementVNode("div", _hoisted_45, [
471
+ vue.createElementVNode("div", _hoisted_39, [
484
472
  (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(items, (relatedTrack, index) => {
485
473
  return vue.openBlock(), vue.createBlock(TrackListCard.default, {
486
474
  key: relatedTrack._id,
@@ -500,7 +488,7 @@ const _sfc_main = {
500
488
  onClosePopup: _cache[2] || (_cache[2] = ($event) => showAddToPlaylistModal.value = false),
501
489
  class: "bg-white pd-medium w-m-25r radius-medium"
502
490
  }, {
503
- default: vue.withCtx(() => _cache[24] || (_cache[24] = [
491
+ default: vue.withCtx(() => _cache[22] || (_cache[22] = [
504
492
  vue.createElementVNode("h3", { class: "h3 mn-b-medium" }, "Add to Playlist", -1),
505
493
  vue.createElementVNode("p", { class: "t-transp" }, "Playlist selector coming soon...", -1)
506
494
  ])),
@@ -1 +1 @@
1
- {"version":3,"file":"Track.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/Track.vue"],"sourcesContent":["<!-- components/pages/Track.vue -->\n<template>\n <div class=\"track-page pd-small\">\n <!-- Loading -->\n <div v-if=\"isLoading\" class=\"w-100 h-25r flex-center flex\">\n <Loader />\n </div>\n\n <!-- Not Found -->\n <div v-if=\"hasLoaded && !track\" class=\"t-center pd-big\">\n <h2 class=\"\">Track not found</h2>\n <p class=\"t-transp t-medium\">The track you're looking for doesn't exist or has been removed.</p>\n </div>\n\n <!-- Track Content -->\n <div v-if=\"track\" class=\"track-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative track-cover-section\">\n <!-- Cover with Play Overlay -->\n <div class=\"cover-container relative mn-b-medium radius-big overflow-hidden shadow-big\">\n <Media \n :url=\"track.coverUrl || (track.album && track.album.coverUrl) || '/logo/logo-placeholder.jpg'\" \n :alt=\"track.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n <!-- <div class=\"cover-overlay absolute inset-0 bg-black-transp-40 flex-center opacity-0 hover-opacity-100 transition\">\n <Button\n @click=\"playTrack\"\n color=\"white\"\n size=\"big\"\n class=\"w-5r h-5r radius-full shadow-big hover-scale-110\"\n >\n <IconPlay class=\"w-2r h-2r\" />\n </Button>\n </div> -->\n </div>\n\n \n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.playCount) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Plays</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.views) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Views</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Track Details -->\n <div class=\"track-details-section\">\n <!-- Track Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase\">Single</span>\n <span v-if=\"track.isExplicit\" class=\"badge bg-danger-transp-20 t-danger pd-thin-big radius-small t-small\">Explicit</span>\n </div>\n\n <!-- Track Title -->\n <h1 class=\"h1 mn-b-medium\">{{ track.title }}</h1>\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playTrack\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 t-white bg-black radius-thin flex-center gap-thin\"\n >\n <IconPlay fill=\"rgb(var(--white))\" class=\"i-medium\" />\n Play\n </Button>\n\n <Button\n @click=\"addToQueue\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconAdd class=\"i-medium\" />\n Add to Queue\n </Button>\n\n <Button\n @click=\"toggleFavorite\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconLike class=\"i-medium\" :fill=\"isFavorite\" />\n {{isFavorite ? 'Liked' : 'Like'}}\n </Button>\n\n <Dropdown :label=\"{component: IconEllipsis, class: 'bg-light radius-thin pd-thin i-big' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"showAddToPlaylistModal = true\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Playlist\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editTrack\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Track\n </Button>\n <Button @click=\"deleteTrack\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Track\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Artist Card -->\n <div class=\"artist-card bg-light pd-medium radius-medium flex items-center gap-medium mn-b-big\">\n <router-link \n :to=\"{ name: 'artist', params: { url: track.artist.url } }\"\n class=\"flex items-center gap-medium flex-1 hover-opacity\"\n >\n <div class=\"artist-avatar\">\n <Media \n v-if=\"track.artist.photoUrl\"\n :src=\"track.artist.photoUrl\"\n :alt=\"track.artist.name\"\n class=\"w-4r h-4r radius-full object-cover\"\n />\n <div v-else class=\"w-4r h-4r radius-full bg-primary flex-center \">\n {{ track.artist.name.charAt(0) }}\n </div>\n </div>\n <div>\n <div class=\"flex items-center gap-thin\">\n <span class=\"t-large \">{{ track.artist.name }}</span>\n <IconVerified v-if=\"track.artist.isVerified\" class=\"w-1r h-1r t-primary\" />\n </div>\n <span class=\"t-small t-transp\">Artist</span>\n </div>\n </router-link>\n <Button \n v-if=\"!isOwner\"\n @click=\"toggleFollowArtist\"\n :color=\"isFollowingArtist ? 'primary' : 'transp'\"\n size=\"small\"\n >\n {{ isFollowingArtist ? 'Following' : 'Follow' }}\n </Button>\n </div>\n\n\n\n <!-- Metadata Cards -->\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-big\">\n <!-- Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconClock class=\"i-regular t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ formatDuration(track.duration) }}</div>\n </div>\n </div>\n\n <!-- Release Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconCalendar class=\"i-regular t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Released</div>\n <div class=\"t-medium \">{{ formatDate(track.releaseDate) }}</div>\n </div>\n </div>\n\n <!-- Status -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-success-transp-20 w-3r h-3r radius-small flex-center\">\n <IconCheck class=\"i-regular t-success\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Status</div>\n <div class=\"t-medium t-success\">{{ track.status }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconEye class=\"i-regular t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ track.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n <!-- Album Info -->\n <div v-if=\"track.album\" class=\"album-card bg-light pd-medium radius-medium mn-b-medium\">\n <div class=\"t-small t-transp t-uppercase mn-b-thin\">From Album</div>\n <router-link \n :to=\"`/album/${track.album.url}`\"\n class=\"flex items-center gap-medium hover-opacity\"\n >\n <Media \n v-if=\"track.album.coverUrl\"\n :src=\"track.album.coverUrl\"\n :alt=\"track.album.title\"\n class=\"w-3r h-3r radius-small object-cover\"\n />\n <span class=\"t-medium \">{{ track.album.title }}</span>\n </router-link>\n </div>\n\n <!-- Genres & Tags -->\n <div v-if=\"(track.genre && track.genre.length) || (track.tags && track.tags.length)\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Genres & Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"genre in track.genre\" \n :key=\"genre\"\n class=\"tag bg-primary-transp-20 t-primary pd-thin-big radius-small t-small hover-bg-primary-transp-30 cursor-pointer\"\n >\n {{ genre }}\n </span>\n <span \n v-for=\"tag in track.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"track.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ track.description }}</p>\n </div>\n\n <!-- Lyrics -->\n <div v-if=\"track.lyrics\" class=\"lyrics-section bg-light pd-medium radius-medium\">\n <h3 class=\"t-medium mn-b-small\">Lyrics</h3>\n <pre class=\"t-transp t-small\">{{ track.lyrics }}</pre>\n </div>\n </div>\n </div>\n\n <!-- Related Tracks -->\n <section v-if=\"track && relatedTracks && relatedTracks.length\" class=\"related-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Related Tracks</h2>\n <Feed\n :store=\"{\n read: () => new Promise(resolve => resolve(relatedTracks || [])),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"relatedTracks\"\n :states=\"{\n empty: {\n title: 'No related tracks',\n description: 'Check back later for recommendations',\n class: 'pd-medium bg-light radius-medium'\n }\n }\"\n class=\"grid cols-2 cols-m-3 cols-l-4 gap-medium\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(relatedTrack, index) in items\"\n :key=\"relatedTrack._id\"\n :track=\"relatedTrack\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- Add to Playlist Modal -->\n <Popup \n v-if=\"showAddToPlaylistModal\" \n @close-popup=\"showAddToPlaylistModal = false\" \n class=\"bg-white pd-medium w-m-25r radius-medium\"\n >\n <h3 class=\"h3 mn-b-medium\">Add to Playlist</h3>\n <!-- <PlaylistSelector \n :trackId=\"track._id\" \n @added=\"showAddToPlaylistModal = false\"\n /> -->\n <p class=\"t-transp\">Playlist selector coming soon...</p>\n </Popup>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Popup from '@martyrs/src/components/Popup/Popup.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconAdd from '@martyrs/src/modules/icons/navigation/IconAdd.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconCheck from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\n// import PlaylistSelector from '../forms/PlaylistSelector.vue';\n\n// Store\nimport { state as tracksState, actions as tracksActions } from '../../store/tracks.js';\nimport { actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst showDropdown = ref(false);\nconst showAddToPlaylistModal = ref(false);\nconst isFavorite = ref(false);\nconst isFollowingArtist = ref(false);\n\n// Clear state\ntracksState.currentTrack = null;\ntracksState.relatedTracks = [];\n\n// Computed\nconst track = computed(() => tracksState.currentTrack);\nconst relatedTracks = computed(() => tracksState.relatedTracks || []);\n\nconst isOwner = computed(() => {\n return track.value?.owner?.target === authState.user?._id;\n});\n\n// Format helpers\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playTrack = () => {\n if (track.value) {\n playerActions.setQueue([track.value]);\n }\n};\n\nconst toggleFavorite = () => {\n isFavorite.value = !isFavorite.value;\n // TODO: Implement actual saving\n};\n\nconst toggleFollowArtist = () => {\n isFollowingArtist.value = !isFollowingArtist.value;\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (track.value) {\n playerActions.addToQueue(track.value);\n }\n};\n\nconst editTrack = () => {\n router.push({ name: 'track-edit', params: { url: track.value.url } });\n};\n\nconst deleteTrack = async () => {\n if (confirm('Are you sure you want to delete this track?')) {\n try {\n await tracksActions.deleteTrack(track.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Failed to delete track:', error);\n }\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\n// Data fetching\nconst fetchTrackData = async () => {\n try {\n await tracksActions.fetchTrackByUrl(route.params.url);\n await tracksActions.fetchRelatedTracks(route.params.url);\n } catch (error) {\n console.error('Error loading track:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchTrackData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>\n\n<style scoped>\n</style>"],"names":["useRoute","useRouter","ref","tracksState","computed","authState","playerActions","tracksActions","onMounted"],"mappings":"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"}
1
+ {"version":3,"file":"Track.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/Track.vue"],"sourcesContent":["<!-- components/pages/Track.vue -->\n<template>\n <div class=\"track-page pd-small\">\n <!-- Loading -->\n <div v-if=\"isLoading\" class=\"w-100 h-25r flex-center flex\">\n <Loader />\n </div>\n\n <!-- Not Found -->\n <div v-if=\"hasLoaded && !track\" class=\"t-center pd-big\">\n <h2 class=\"\">Track not found</h2>\n <p class=\"t-transp t-medium\">The track you're looking for doesn't exist or has been removed.</p>\n </div>\n\n <!-- Track Content -->\n <div v-if=\"track\" class=\"track-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative track-cover-section\">\n <!-- Cover with Play Overlay -->\n <div class=\"cover-container pos-relative mn-b-medium radius-big o-hidden\">\n <Media \n :url=\"track.coverUrl || (track.album && track.album.coverUrl) || '/logo/logo-placeholder.jpg'\" \n :alt=\"track.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n <div class=\"cover-overlay w-100 h-100 pos-absolute pos-t-0 pos-r-0 bg-black-transp-40 flex flex-center\">\n <Button\n @click=\"playTrack\"\n color=\"white\"\n size=\"big\"\n class=\"w-5r h-5r radius-big bg-main shadow-big hover-scale-110\"\n >\n <IconPlay v-if=\"!isPlaying\" fill=\"rgb(var(--white))\" class=\"i-medium\" />\n <IconPause v-else fill=\"rgb(var(--white))\" class=\"i-medium\" />\n </Button>\n </div>\n </div>\n\n \n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.playCount) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Plays</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.views) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Views</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Track Details -->\n <div class=\"track-details-section\">\n <!-- Track Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase\">Single</span>\n <span class=\"bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase\">Explicit</span>\n </div>\n\n <!-- Track Title -->\n <h1 class=\"h1 mn-b-medium\">{{ track.title }}</h1>\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playTrack\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 t-white bg-black radius-thin flex-center gap-thin\"\n >\n <IconPlay v-if=\"!isPlaying\" fill=\"rgb(var(--white))\" class=\"i-medium\" />\n <IconPause v-else fill=\"rgb(var(--white))\" class=\"i-medium\" />\n {{ !isPlaying ? 'Play' : 'Pause'}}\n </Button>\n\n <Button\n @click=\"addToQueue\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconAdd class=\"i-medium\" />\n Add to Queue\n </Button>\n\n <Button\n @click=\"toggleFavorite\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconLike class=\"i-medium\" :fill=\"isFavorite ? 'rgb(var(--main)':'rgb(var(--black)'\" />\n {{isFavorite ? 'Liked' : 'Like'}}\n </Button>\n\n <Dropdown :label=\"{component: IconEllipsis, class: 'bg-light radius-thin pd-thin i-big' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"showAddToPlaylistModal = true\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Playlist\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editTrack\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Track\n </Button>\n <Button @click=\"deleteTrack\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Track\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Artist Card -->\n <div class=\"artists-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\" v-if=\"track.artist\">Artist</h3>\n <div class=\"flex flex-col gap-small\">\n <ArtistCardSmall \n :key=\"track.artist._id\"\n :artist=\"track.artist\"\n :is-following=\"isFollowingArtist\"\n :show-follow-button=\"!isOwner\"\n @toggle-follow=\"toggleFollowArtist\"\n />\n </div>\n </div>\n\n <!-- Metadata Cards -->\n <h3 class=\"t-medium mn-b-small\">Metadata</h3>\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-medium\">\n <!-- Release Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCalendar class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Released</div>\n <div class=\"t-medium \">{{ formatDate(track.releaseDate) }}</div>\n </div>\n </div>\n\n <!-- Total Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconClock class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ formatDuration(track.duration) }}</div>\n </div>\n </div>\n\n <!-- Label -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCheck class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Status</div>\n <div class=\"t-medium \">{{ track.status }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconEye class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ track.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n\n <!-- Album Info -->\n <h3 v-if=\"track.album\" class=\"t-medium mn-b-small\">From Album</h3>\n <div v-if=\"track.album\" class=\"album-card bg-light pd-medium radius-medium mn-b-medium\">\n \n <router-link \n :to=\"`/album/${track.album.url}`\"\n class=\"flex flex-v-center gap-thin hover-opacity\"\n >\n <Media \n :url=\"track.album.coverArt || '/logo/logo-placeholder.jpg'\"\n :alt=\"track.album.title\"\n class=\"w-3r h-3r radius-thin o-hidden object-cover\"\n />\n <span class=\"t-medium \">{{ track.album.title }}</span>\n </router-link>\n </div>\n\n <!-- Genres & Tags -->\n <div v-if=\"(track.genre && track.genre.length) || (track.tags && track.tags.length)\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Genres & Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"genre in track.genre\" \n :key=\"genre\"\n class=\"tag bg-main t-medium pd-thin radius-thin t-small cursor-pointer\"\n >\n {{ genre.name || genre }}\n </span>\n <span \n v-for=\"tag in track.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag.name || tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"track.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ track.description }}</p>\n </div>\n\n <!-- Lyrics -->\n <div v-if=\"track.lyrics\" class=\"lyrics-section bg-light pd-medium radius-medium\">\n <h3 class=\"t-medium mn-b-small\">Lyrics</h3>\n <pre class=\"t-transp t-small\">{{ track.lyrics }}</pre>\n </div>\n </div>\n </div>\n\n <!-- Related Tracks -->\n <section v-if=\"track && relatedTracks && relatedTracks.length\" class=\"related-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Related Tracks</h2>\n <Feed\n :store=\"{\n read: () => new Promise(resolve => resolve(relatedTracks || [])),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"relatedTracks\"\n :states=\"{\n empty: {\n title: 'No related tracks',\n description: 'Check back later for recommendations',\n class: 'pd-medium bg-light radius-medium'\n }\n }\"\n class=\"grid cols-2 cols-m-3 cols-l-4 gap-medium\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(relatedTrack, index) in items\"\n :key=\"relatedTrack._id\"\n :track=\"relatedTrack\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- Add to Playlist Modal -->\n <Popup \n v-if=\"showAddToPlaylistModal\" \n @close-popup=\"showAddToPlaylistModal = false\" \n class=\"bg-white pd-medium w-m-25r radius-medium\"\n >\n <h3 class=\"h3 mn-b-medium\">Add to Playlist</h3>\n <!-- <PlaylistSelector \n :trackId=\"track._id\" \n @added=\"showAddToPlaylistModal = false\"\n /> -->\n <p class=\"t-transp\">Playlist selector coming soon...</p>\n </Popup>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Popup from '@martyrs/src/components/Popup/Popup.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconPause from '@martyrs/src/modules/icons/navigation/IconPause.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconAdd from '@martyrs/src/modules/icons/navigation/IconAdd.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconCheck from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport ArtistCardSmall from '../cards/ArtistCardSmall.vue';\n// import PlaylistSelector from '../forms/PlaylistSelector.vue';\n\n// Store\nimport { state as tracksState, actions as tracksActions } from '../../store/tracks.js';\nimport { state as playerState, actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\n\nconst isPlaying = computed(() => playerState.isPlaying);\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst showDropdown = ref(false);\nconst showAddToPlaylistModal = ref(false);\nconst isFavorite = ref(false);\nconst isFollowingArtist = ref(false);\n\n// Clear state\ntracksState.currentTrack = null;\ntracksState.relatedTracks = [];\n\n// Computed\nconst track = computed(() => tracksState.currentTrack);\nconst relatedTracks = computed(() => tracksState.relatedTracks || []);\n\nconst isOwner = computed(() => {\n return track.value?.owner?.target === authState.user?._id;\n});\n\n// Format helpers\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playTrack = () => {\n if (track.value) {\n playerActions.setQueue([track.value]);\n }\n};\n\nconst toggleFavorite = () => {\n isFavorite.value = !isFavorite.value;\n // TODO: Implement actual saving\n};\n\nconst toggleFollowArtist = () => {\n isFollowingArtist.value = !isFollowingArtist.value;\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (track.value) {\n playerActions.addToQueue(track.value);\n }\n};\n\nconst editTrack = () => {\n router.push({ name: 'track-edit', params: { url: track.value.url } });\n};\n\nconst deleteTrack = async () => {\n if (confirm('Are you sure you want to delete this track?')) {\n try {\n await tracksActions.deleteTrack(track.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Failed to delete track:', error);\n }\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\n// Data fetching\nconst fetchTrackData = async () => {\n try {\n await tracksActions.fetchTrackByUrl(route.params.url);\n await tracksActions.fetchRelatedTracks(route.params.url);\n } catch (error) {\n console.error('Error loading track:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchTrackData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>\n\n<style scoped>\n</style>"],"names":["computed","playerState","useRoute","useRouter","ref","tracksState","authState","playerActions","tracksActions","onMounted"],"mappings":"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"}