updated history page
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -868,19 +868,18 @@ def api_delete_post(post_id: int):
|
||||
return _error("You don't have permission to delete this post.", 403)
|
||||
|
||||
try:
|
||||
add_audit_log({
|
||||
"user_id": user_id,
|
||||
"action": "post.deleted",
|
||||
"details": json.dumps({"deleted_post_id": post_id, "title": post.get("title")})
|
||||
})
|
||||
|
||||
delete_rag_chunks(post_id)
|
||||
delete_archive_files(post_id)
|
||||
delete_metadata(post_id)
|
||||
delete_rights(post_id)
|
||||
delete_audio_post(post_id)
|
||||
|
||||
add_audit_log({
|
||||
"post_id": post_id,
|
||||
"user_id": user_id,
|
||||
"action": "post.deleted",
|
||||
"details": json.dumps({"title": post.get("title")})
|
||||
})
|
||||
|
||||
return jsonify({"message": "Post deleted successfully", "post_id": post_id})
|
||||
|
||||
except Exception as e:
|
||||
|
||||
BIN
backend/uploads/00ce7e87-614d-44f7-a300-0ac8fb4af2bd_data.m4a
Normal file
BIN
backend/uploads/00ce7e87-614d-44f7-a300-0ac8fb4af2bd_data.m4a
Normal file
Binary file not shown.
BIN
backend/uploads/28b3ced4-4174-4555-ad9c-cb0adff1fbba_harvard.wav
Normal file
BIN
backend/uploads/28b3ced4-4174-4555-ad9c-cb0adff1fbba_harvard.wav
Normal file
Binary file not shown.
BIN
frontend/public/Logo.png
Normal file
BIN
frontend/public/Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 421 KiB |
@@ -61,6 +61,7 @@ export default function App() {
|
||||
localStorage.removeItem('voicevault_user')
|
||||
setShowLogin(true)
|
||||
setActiveTab('feed')
|
||||
setViewingPostId(null)
|
||||
}
|
||||
|
||||
const handleSearch = async (query) => {
|
||||
@@ -91,9 +92,15 @@ export default function App() {
|
||||
setViewingPostId(null)
|
||||
}
|
||||
|
||||
const handleTabChange = (tab) => {
|
||||
setActiveTab(tab)
|
||||
setViewingPostId(null)
|
||||
}
|
||||
|
||||
const handlePostCreated = () => {
|
||||
// Switch to feed after creating a post
|
||||
setActiveTab('feed')
|
||||
setViewingPostId(null)
|
||||
}
|
||||
|
||||
const handleUserUpdate = (updatedUser) => {
|
||||
@@ -202,11 +209,16 @@ export default function App() {
|
||||
|
||||
// Main App
|
||||
return (
|
||||
<div className="h-screen bg-gray-50 text-gray-800 flex flex-col overflow-hidden">
|
||||
<div className="h-screen bg-white-50 text-gray-800 flex flex-col overflow-hidden">
|
||||
<Header onSearch={handleSearch} onLogout={handleLogout} onNavigateToSearch={handleNavigateToSearch} />
|
||||
|
||||
<<<<<<< HEAD
|
||||
<div className="flex-1 flex overflow-hidden max-w-[1400px] mx-auto w-full">
|
||||
<Sidebar user={user} activeTab={activeTab} onTabChange={handleTabChange} />
|
||||
=======
|
||||
<div className="flex-1 flex overflow-hidden max-w-[1500px] mx-auto w-full">
|
||||
<Sidebar user={user} activeTab={activeTab} onTabChange={setActiveTab} />
|
||||
>>>>>>> cd657f37cf76773d9dd6ad7a0cdbe05e19a9eced
|
||||
|
||||
<main className="flex-1 overflow-y-auto p-6">
|
||||
{renderPage()}
|
||||
|
||||
@@ -27,13 +27,16 @@ class ApiClient {
|
||||
|
||||
try {
|
||||
const response = await fetch(url, config);
|
||||
const data = await response.json();
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
const isJson = contentType.includes('application/json');
|
||||
const data = isJson ? await response.json() : null;
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Request failed');
|
||||
const fallbackError = `Request failed with status ${response.status}`;
|
||||
throw new Error((data && data.error) || fallbackError);
|
||||
}
|
||||
|
||||
return data;
|
||||
return data || {};
|
||||
} catch (error) {
|
||||
console.error('API Error:', error);
|
||||
throw error;
|
||||
@@ -87,14 +90,16 @@ class ApiClient {
|
||||
method: 'POST',
|
||||
body: formData, // Don't set Content-Type, let browser set it with boundary
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
const isJson = contentType.includes('application/json');
|
||||
const data = isJson ? await response.json() : null;
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Upload failed');
|
||||
const fallbackError = `Upload failed with status ${response.status}`;
|
||||
throw new Error((data && data.error) || fallbackError);
|
||||
}
|
||||
|
||||
return data;
|
||||
return data || {};
|
||||
} catch (error) {
|
||||
console.error('Upload Error:', error);
|
||||
throw error;
|
||||
@@ -144,10 +149,6 @@ class ApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
async getPostMetadata(postId) {
|
||||
return this.request(`/posts/${postId}/metadata`);
|
||||
}
|
||||
|
||||
async getAudioUrl(postId, expiresIn = 3600) {
|
||||
return this.request(`/posts/${postId}/audio?expires_in=${expiresIn}`);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ export default function EditPostModal({ post, user, onClose, onSave }) {
|
||||
visibility
|
||||
}
|
||||
|
||||
await api.editPost(post.post_id, updates)
|
||||
onSave?.()
|
||||
const updatedPost = await api.editPost(post.post_id, updates)
|
||||
onSave?.(updatedPost)
|
||||
onClose()
|
||||
} catch (err) {
|
||||
setError(err.message || 'Failed to update post')
|
||||
|
||||
@@ -23,10 +23,15 @@ export default function Header({ onSearch, onLogout, onNavigateToSearch }) {
|
||||
<div className="max-w-[1400px] mx-auto flex items-center justify-between gap-6">
|
||||
{/* Left: Logo */}
|
||||
<div className="flex items-center gap-3 flex-shrink-0">
|
||||
<div className="w-8 h-8 bg-[#f4b840] rounded-lg flex items-center justify-center">
|
||||
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<div className="w-8 h-8 rounded-lg flex items-center justify-center">
|
||||
{/* <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
|
||||
</svg>
|
||||
</svg> */}
|
||||
<img
|
||||
src="/Logo.png"
|
||||
alt="VoiceVault Logo"
|
||||
className="w-10 h-10 object-contain rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<h1 className="text-lg font-bold text-gray-900">VoiceVault</h1>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function History({ user, onViewPost }) {
|
||||
try {
|
||||
await api.deletePost(postId, user.user_id)
|
||||
// Remove from local state immediately
|
||||
setPosts(posts.filter(p => p.post_id !== postId))
|
||||
setPosts((prev) => prev.filter((p) => p.post_id !== postId))
|
||||
} catch (err) {
|
||||
alert('Failed to delete post: ' + err.message)
|
||||
} finally {
|
||||
@@ -58,8 +58,15 @@ export default function History({ user, onViewPost }) {
|
||||
setEditingPost(post)
|
||||
}
|
||||
|
||||
const handleSaveEdit = () => {
|
||||
// Refresh the list after edit
|
||||
const handleSaveEdit = (updatedPost) => {
|
||||
if (updatedPost && updatedPost.post_id) {
|
||||
setPosts((prev) =>
|
||||
prev.map((post) =>
|
||||
post.post_id === updatedPost.post_id ? { ...post, ...updatedPost } : post
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
fetchHistory()
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export default function PostDetail({ postId, user, onBack }) {
|
||||
const [volume, setVolume] = useState(1)
|
||||
const [downloading, setDownloading] = useState(false)
|
||||
const audioRef = useRef(null)
|
||||
const [audioSrc, setAudioSrc] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (postId) {
|
||||
@@ -34,6 +35,8 @@ export default function PostDetail({ postId, user, onBack }) {
|
||||
|
||||
// Load audio URL if available
|
||||
if (postData.audio_url && audioRef.current) {
|
||||
setPost(postData)
|
||||
setAudioSrc(postData.audio_url)
|
||||
audioRef.current.src = postData.audio_url
|
||||
audioRef.current.load()
|
||||
}
|
||||
@@ -290,12 +293,14 @@ export default function PostDetail({ postId, user, onBack }) {
|
||||
|
||||
<audio
|
||||
ref={audioRef}
|
||||
src={audioSrc}
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onLoadedMetadata={handleLoadedMetadata}
|
||||
onEnded={handleEnded}
|
||||
preload="metadata"
|
||||
/>
|
||||
|
||||
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
|
||||
Reference in New Issue
Block a user