1- import React from 'react' ;
2- import { PlayIcon , PauseIcon } from '@heroicons/react/24/solid' ;
1+ import React , { useState } from 'react' ;
2+ import { PlayIcon , PauseIcon , TrashIcon } from '@heroicons/react/24/solid' ;
33import { SampleDTO } from '../../models/RecordingsDTO' ;
4+ import { deleteSample } from '../../services/apis/recordingService' ;
45import './AudioSamples.scss' ;
56
67type Props = {
78 samples : SampleDTO [ ] ;
89 albumId : string ;
10+ recordingId : number ;
911 playingId : string | null ;
1012 setPlayingId : ( id : string | null ) => void ;
1113 audioRefs : React . MutableRefObject < { [ id : string ] : HTMLAudioElement | null } > ;
14+ isAdmin ?: boolean ;
15+ adminToken ?: string ;
16+ deleteProtectionEnabled ?: boolean ;
17+ onSampleDeleted ?: ( sampleId : number ) => void ;
1218} ;
1319
1420const formatTime = ( seconds : number ) => {
@@ -17,9 +23,12 @@ const formatTime = (seconds: number) => {
1723 return `${ mins } :${ secs . toString ( ) . padStart ( 2 , '0' ) } ` ;
1824} ;
1925
20- const AudioSamples : React . FC < Props > = ( { samples, albumId, playingId, setPlayingId, audioRefs } ) => {
26+ const AudioSamples : React . FC < Props > = ( { samples, albumId, recordingId, playingId, setPlayingId, audioRefs, isAdmin, adminToken, deleteProtectionEnabled = true , onSampleDeleted } ) => {
27+ const [ sampleToDelete , setSampleToDelete ] = useState < SampleDTO | null > ( null ) ;
28+ const [ deleting , setDeleting ] = useState ( false ) ;
29+ const [ deleteError , setDeleteError ] = useState < string | null > ( null ) ;
30+
2131 if ( samples . length === 0 ) return null ;
22- const limitedSamples = samples . slice ( 0 , 4 ) ;
2332
2433 const handlePlayPause = ( index : number ) => {
2534 const id = `${ albumId } -${ index } ` ;
@@ -40,9 +49,30 @@ const AudioSamples: React.FC<Props> = ({ samples, albumId, playingId, setPlaying
4049 if ( playingId === id ) setPlayingId ( null ) ;
4150 } ;
4251
52+ const handleDeleteSample = async ( sample : SampleDTO ) => {
53+ if ( ! sample . id ) return ;
54+ setDeleting ( true ) ;
55+ setDeleteError ( null ) ;
56+ try {
57+ await deleteSample ( recordingId , sample . id , adminToken ) ;
58+ onSampleDeleted ?.( sample . id ) ;
59+ setSampleToDelete ( null ) ;
60+ } catch ( err : any ) {
61+ setDeleteError ( 'Failed to delete sample: ' + ( err . message || 'Unknown error' ) ) ;
62+ } finally {
63+ setDeleting ( false ) ;
64+ }
65+ } ;
66+
4367 return (
68+ < >
4469 < div className = "scroll-container space-y-2" >
45- { limitedSamples . map ( ( sample , index ) => {
70+ { deleteError && (
71+ < div className = "bg-red-700 text-white rounded px-3 py-2 text-sm mb-2" >
72+ { deleteError }
73+ </ div >
74+ ) }
75+ { samples . map ( ( sample , index ) => {
4676 const id = `${ albumId } -${ index } ` ;
4777 return (
4878 < div key = { id } className = "flex items-center gap-3 p-2 bg-black/20 rounded-md hover:bg-black/30 transition-colors" >
@@ -79,10 +109,54 @@ const AudioSamples: React.FC<Props> = ({ samples, albumId, playingId, setPlaying
79109 ) : (
80110 < div className = "text-xs text-neutral-300" > Sample unavailable</ div >
81111 ) }
112+ { isAdmin && sample . id && (
113+ < button
114+ onClick = { async ( e ) => {
115+ e . stopPropagation ( ) ;
116+ if ( deleteProtectionEnabled ) {
117+ setSampleToDelete ( sample ) ;
118+ } else {
119+ await handleDeleteSample ( sample ) ;
120+ }
121+ } }
122+ className = "flex items-center justify-center w-6 h-6 hover:bg-yellow-600/30 rounded transition-colors flex-shrink-0"
123+ aria-label = "Delete sample"
124+ disabled = { deleting }
125+ >
126+ < TrashIcon className = "h-4 w-4 text-yellow-400 hover:text-yellow-300" />
127+ </ button >
128+ ) }
82129 </ div >
83130 ) ;
84131 } ) }
85132 </ div >
133+
134+ { /* Delete Confirmation Modal */ }
135+ { deleteProtectionEnabled && sampleToDelete && (
136+ < div className = "fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-70" >
137+ < div className = "bg-gray-900 rounded-lg shadow-lg p-8 max-w-md w-full text-center border border-yellow-400" >
138+ < h2 className = "text-xl text-yellow-300 mb-4 font-semibold" > Confirm Delete</ h2 >
139+ < p className = "text-white mb-6" > Are you sure you want to delete sample < span className = "font-bold" > { sampleToDelete . trackName } </ span > ?</ p >
140+ < div className = "flex justify-center gap-6" >
141+ < button
142+ className = "bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded font-bold"
143+ disabled = { deleting }
144+ onClick = { ( ) => handleDeleteSample ( sampleToDelete ) }
145+ >
146+ { deleting ? 'Deleting...' : 'Delete' }
147+ </ button >
148+ < button
149+ className = "bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded font-bold"
150+ onClick = { ( ) => setSampleToDelete ( null ) }
151+ disabled = { deleting }
152+ >
153+ Cancel
154+ </ button >
155+ </ div >
156+ </ div >
157+ </ div >
158+ ) }
159+ </ >
86160 ) ;
87161} ;
88162
0 commit comments