From 2e59823caa3660be435267088c84c98359f622ff Mon Sep 17 00:00:00 2001 From: borrageiros Date: Wed, 14 May 2025 21:07:48 +0200 Subject: [PATCH] SAVE --- public/locales/en/common.json | 9 +- public/locales/es/common.json | 9 +- .../DrawingCard/DrawingCard.module.css | 613 ++++++++++-------- src/components/DrawingCard/DrawingCard.tsx | 213 ++++-- 4 files changed, 528 insertions(+), 316 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index a31052b..b35a18d 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -85,7 +85,8 @@ "deleteFailed": "Failed to delete drawing.", "loadUsersFailed": "Failed to load users.", "userNotSelected": "Please select a user to share with.", - "shareFailed": "Failed to share drawing." + "shareFailed": "Failed to share drawing.", + "unshareFailed": "Failed to unshare drawing." }, "shareModal": { "title": "Share Drawing", @@ -94,7 +95,11 @@ "cancelButton": "Cancel", "noUsersFound": "No users available to share with or match your search.", "loadingUsers": "Loading users...", - "sharingButton": "Sharing..." + "sharingButton": "Sharing...", + "shareTab": "Share", + "manageSharesTab": "Manage Access", + "noSharedUsers": "This drawing is not shared with anyone yet.", + "unsharingButton": "Removing access..." } }, "editor": { diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 13d9f19..857e39a 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -85,7 +85,8 @@ "deleteFailed": "Error al eliminar el dibujo.", "loadUsersFailed": "Error al cargar usuarios.", "userNotSelected": "Por favor, selecciona un usuario para compartir.", - "shareFailed": "Error al compartir el dibujo." + "shareFailed": "Error al compartir el dibujo.", + "unshareFailed": "Error al dejar de compartir el dibujo." }, "shareModal": { "title": "Compartir Dibujo", @@ -94,7 +95,11 @@ "cancelButton": "Cancelar", "noUsersFound": "No hay usuarios disponibles para compartir o que coincidan con tu búsqueda.", "loadingUsers": "Cargando usuarios...", - "sharingButton": "Compartiendo..." + "sharingButton": "Compartiendo...", + "shareTab": "Compartir", + "manageSharesTab": "Gestionar Accesos", + "noSharedUsers": "Este dibujo no está compartido con nadie todavía.", + "unsharingButton": "Eliminando acceso..." } }, "editor": { diff --git a/src/components/DrawingCard/DrawingCard.module.css b/src/components/DrawingCard/DrawingCard.module.css index 5e69987..e5fd615 100644 --- a/src/components/DrawingCard/DrawingCard.module.css +++ b/src/components/DrawingCard/DrawingCard.module.css @@ -1,417 +1,488 @@ .cardContainer { - border: 1px solid #e0e0e0; + background-color: var(--card-bg, #ffffff); border-radius: 8px; - background-color: #ffffff; - padding: 1rem; - transition: box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out, background-color 0.3s ease, border-color 0.3s ease; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: transform 0.2s ease, box-shadow 0.2s ease; display: flex; flex-direction: column; + height: 100%; } :global(.dark) .cardContainer { - background-color: #1e1e1e; - border-color: #333; + --card-bg: #1e1e1e; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } .cardContainer:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } :global(.dark) .cardContainer:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); } .titleContainer { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 0.75rem; + padding: 1rem; + border-bottom: 1px solid var(--border-color, #eaeaea); +} + +:global(.dark) .titleContainer { + --border-color: #333; +} + +.cardTitle { + margin: 0; + font-size: 1.2rem; + font-weight: 600; + color: var(--text-primary, #333); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +:global(.dark) .cardTitle { + --text-primary: #e0e0e0; } .titleLink { text-decoration: none; color: inherit; - flex-grow: 1; - margin-right: 0.5rem; + flex: 1; + min-width: 0; } -.cardTitle { - font-size: 1.2rem; - font-weight: 600; - color: #333; +.cardContent { + padding: 1rem; + flex: 1; + display: flex; + flex-direction: column; +} + +.cardDate { margin: 0; - word-break: break-word; -} - -:global(.dark) .cardTitle { - color: #e0e0e0; -} - -.editButton { - background: none; - border: none; - padding: 0.25rem; - cursor: pointer; - color: #555; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; -} - -.editButton:hover { - color: #000; - background-color: #f0f0f0; -} - -.actionButtonsContainer { - display: flex; - gap: 0.25rem; -} - -.iconButton { - background: none; - border: none; - padding: 0.25rem; - cursor: pointer; - color: #555; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - transition: background-color 0.2s ease, color 0.2s ease; -} - -:global(.dark) .iconButton { - color: #aaa; -} - -.iconButton:hover { - color: #000; - background-color: #f0f0f0; -} - -:global(.dark) .iconButton:hover { - color: #fff; - background-color: #333; -} - -.deleteButton:hover { - color: #d9534f; -} - -:global(.dark) .deleteButton:hover { - color: #ff7875; -} - -.inlineEditForm { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 0.75rem; -} - -.inlineInput { - padding: 0.5rem 0.7rem; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 1rem; - flex-grow: 1; - background-color: #fff; - color: #333; -} - -:global(.dark) .inlineInput { - background-color: #2a2a2a; - border-color: #444; - color: #e0e0e0; -} - -.inlineInput:focus { - outline: none; - border-color: #0070f3; - box-shadow: 0 0 0 2px rgba(0, 112, 243, 0.2); -} - -:global(.dark) .inlineInput:focus { - border-color: #0070f3; - box-shadow: 0 0 0 2px rgba(0, 112, 243, 0.3); -} - -.formActionButton { - padding: 0.5rem 0.9rem; - border: none; - border-radius: 4px; font-size: 0.85rem; - font-weight: 500; - cursor: pointer; - transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, transform 0.1s ease; + color: var(--text-secondary, #666); } -.formActionButton:active { - transform: translateY(1px); -} - -.confirmButton { - background-color: #28a745; - color: white; -} - -.confirmButton:hover { - background-color: #218838; -} - -.cancelButton { - background-color: #6c757d; - color: white; -} - -.cancelButton:hover { - background-color: #5a6268; +:global(.dark) .cardDate { + --text-secondary: #aaa; } .cardLinkUnderTitle { text-decoration: none; color: inherit; + flex: 1; + display: flex; + flex-direction: column; } -.cardContent { - margin-top: auto; +.actionButtonsContainer { + display: flex; + gap: 0.5rem; } -.cardDate { - font-size: 0.8rem; - color: #777; - margin-top: 0.5rem; -} - -:global(.dark) .cardDate { - color: #aaa; -} - -.renameErrorText { - color: #d9534f; - font-size: 0.8rem; - margin-bottom: 0.5rem; -} - -:global(.dark) .renameErrorText { - color: #ff7875; -} - -.thumbnailPlaceholder { - width: 100%; - height: 150px; - background-color: #f0f0f0; +.iconButton { + background: none; + border: none; + cursor: pointer; display: flex; align-items: center; justify-content: center; - color: #999; + padding: 0.25rem; border-radius: 4px; - margin-bottom: 12px; + color: var(--icon-color, #666); + transition: background-color 0.2s ease, color 0.2s ease; } -:global(.dark) .thumbnailPlaceholder { - background-color: #2a2a2a; - color: #aaa; +:global(.dark) .iconButton { + --icon-color: #aaa; } -.cardActions { - margin-top: 16px; - display: flex; - justify-content: flex-end; +.iconButton:hover { + background-color: var(--icon-hover-bg, rgba(0, 0, 0, 0.05)); + color: var(--icon-hover-color, #333); +} + +:global(.dark) .iconButton:hover { + --icon-hover-bg: rgba(255, 255, 255, 0.1); + --icon-hover-color: #fff; +} + +.deleteButton:hover { + color: var(--delete-color, #e53935); +} + +:global(.dark) .deleteButton:hover { + --delete-color: #f44336; +} + +.inlineEditForm { + padding: 0.75rem; + width: 100%; + border-bottom: 1px solid var(--border-color, #eaeaea); +} + +:global(.dark) .inlineEditForm { + --border-color: #333; +} + +.inlineInput { + width: 100%; + padding: 0.5rem; + border: 1px solid var(--input-border, #ccc); + border-radius: 4px; + font-size: 1rem; + background-color: var(--input-bg, #fff); + color: var(--input-color, #333); +} + +:global(.dark) .inlineInput { + --input-border: #555; + --input-bg: #2a2a2a; + --input-color: #e0e0e0; +} + +.inlineInput:focus { + outline: none; + border-color: var(--input-focus-border, #0070f3); + box-shadow: 0 0 0 2px var(--input-focus-shadow, rgba(0, 112, 243, 0.2)); +} + +:global(.dark) .inlineInput:focus { + --input-focus-border: #0070f3; + --input-focus-shadow: rgba(0, 112, 243, 0.3); +} + +.renameErrorText { + color: var(--error-color, #ff4d4f); + font-size: 0.85rem; + margin: 0.5rem 0; + text-align: center; +} + +:global(.dark) .renameErrorText { + --error-color: #ff7875; } .modalErrorText { - color: #d9534f; + color: var(--error-color, #ff4d4f); font-size: 0.9rem; - margin: 0.5rem 0; + margin: 1rem 0; padding: 0.5rem; - background-color: rgba(217, 83, 79, 0.1); + background-color: var(--error-bg, rgba(255, 77, 79, 0.1)); border-radius: 4px; + text-align: center; } :global(.dark) .modalErrorText { - color: #ff7875; - background-color: rgba(255, 120, 117, 0.15); + --error-color: #ff7875; + --error-bg: rgba(255, 77, 79, 0.2); } .modalFooter { display: flex; justify-content: flex-end; gap: 0.75rem; - margin-top: 1rem; + margin-top: 1.5rem; } .modalButtonCancel { - padding: 0.6rem 1.2rem; - border-radius: 6px; - font-weight: 500; + padding: 0.5rem 1rem; + background-color: var(--cancel-btn-bg, #f5f5f5); + color: var(--cancel-btn-color, #333); + border: 1px solid var(--cancel-btn-border, #d9d9d9); + border-radius: 4px; cursor: pointer; - background-color: #f8f9fa; - color: #333; - border: 1px solid #ced4da; - transition: background-color 0.2s ease, border-color 0.2s ease; + font-size: 0.9rem; + transition: all 0.2s; } :global(.dark) .modalButtonCancel { - background-color: #333; - color: #e0e0e0; - border-color: #555; + --cancel-btn-bg: #333; + --cancel-btn-color: #e0e0e0; + --cancel-btn-border: #444; } .modalButtonCancel:hover { - background-color: #e2e6ea; - border-color: #dae0e5; + background-color: var(--cancel-btn-hover-bg, #e6e6e6); } :global(.dark) .modalButtonCancel:hover { - background-color: #444; - border-color: #666; + --cancel-btn-hover-bg: #444; +} + +.modalButtonCancel:disabled { + opacity: 0.6; + cursor: not-allowed; } .modalButtonConfirm { - padding: 0.6rem 1.2rem; - border-radius: 6px; - font-weight: 500; - cursor: pointer; - background-color: #d9534f; + padding: 0.5rem 1rem; + background-color: var(--confirm-btn-bg, #1890ff); color: white; - border: 1px solid #d9534f; - transition: background-color 0.2s ease, border-color 0.2s ease; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; + transition: all 0.2s; +} + +.modalButtonConfirm.delete { + --confirm-btn-bg: #ff4d4f; } .modalButtonConfirm:hover { - background-color: #c9302c; - border-color: #ac2925; + background-color: var(--confirm-btn-hover-bg, #40a9ff); +} + +.modalButtonConfirm.delete:hover { + --confirm-btn-hover-bg: #ff7875; +} + +:global(.dark) .modalButtonConfirm:hover { + filter: brightness(1.1); +} + +.modalButtonConfirm:disabled { + opacity: 0.6; + cursor: not-allowed; } -/* Share modal */ .shareModalContent { - display: flex; - flex-direction: column; - gap: 1rem; + margin-bottom: 1rem; } .shareSearchInput { - padding: 0.75rem; - border: 1px solid #ced4da; - border-radius: 6px; - font-size: 0.95rem; width: 100%; - transition: border-color 0.2s ease, box-shadow 0.2s ease; + padding: 0.75rem; + border: 1px solid var(--input-border, #d9d9d9); + border-radius: 4px; + font-size: 0.9rem; + margin-bottom: 1rem; + background-color: var(--input-bg, #fff); + color: var(--input-color, #333); +} + +:global(.dark) .shareSearchInput { + --input-border: #444; + --input-bg: #2a2a2a; + --input-color: #e0e0e0; } .shareSearchInput:focus { outline: none; - border-color: #0070f3; - box-shadow: 0 0 0 2px rgba(0, 112, 243, 0.2); -} - -:global(.dark) .shareSearchInput { - background-color: #2a2a2a; - border-color: #444; - color: #e0e0e0; + border-color: var(--input-focus-border, #1890ff); + box-shadow: 0 0 0 2px var(--input-focus-shadow, rgba(24, 144, 255, 0.2)); } :global(.dark) .shareSearchInput:focus { - border-color: #0070f3; - box-shadow: 0 0 0 2px rgba(0, 112, 243, 0.3); + --input-focus-border: #1890ff; + --input-focus-shadow: rgba(24, 144, 255, 0.3); } .shareSearchInput:disabled { - background-color: #f8f9fa; + background-color: var(--input-disabled-bg, #f5f5f5); cursor: not-allowed; - opacity: 0.7; } :global(.dark) .shareSearchInput:disabled { - background-color: #333; + --input-disabled-bg: #333; } .userListShare { list-style: none; padding: 0; margin: 0; - max-height: 250px; + max-height: 200px; overflow-y: auto; - border: 1px solid #ced4da; - border-radius: 6px; + border: 1px solid var(--list-border, #d9d9d9); + border-radius: 4px; } :global(.dark) .userListShare { - border-color: #444; + --list-border: #444; } .userListItemShare { - padding: 0.75rem 1rem; + padding: 0.75rem; cursor: pointer; - transition: background-color 0.2s ease; - border-bottom: 1px solid #eeeeee; + border-bottom: 1px solid var(--item-border, #f0f0f0); + background-color: var(--item-bg, #fff); + color: var(--item-color, #333); + transition: background-color 0.2s; +} + +:global(.dark) .userListItemShare { + --item-border: #333; + --item-bg: #2a2a2a; + --item-color: #e0e0e0; } .userListItemShare:last-child { border-bottom: none; } -:global(.dark) .userListItemShare { - border-bottom-color: #333; -} - .userListItemShare:hover { - background-color: #f8f9fa; + background-color: var(--item-hover-bg, #f5f5f5); } :global(.dark) .userListItemShare:hover { - background-color: #333; + --item-hover-bg: #333; } -.selectedUser { - background-color: #e8f0fe; +.userListItemShare.selectedUser { + background-color: var(--selected-bg, #e6f7ff); + color: var(--selected-color, #1890ff); +} + +:global(.dark) .userListItemShare.selectedUser { + --selected-bg: rgba(24, 144, 255, 0.2); + --selected-color: #1890ff; +} + +.userListItemShare.disabledItem { + opacity: 0.7; + cursor: not-allowed; +} + +.tabsContainer { + display: flex; + margin-bottom: 1rem; + border-bottom: 1px solid var(--tabs-border, #d9d9d9); +} + +:global(.dark) .tabsContainer { + --tabs-border: #444; +} + +.tabButton { + padding: 0.75rem 1.5rem; + background: none; + border: none; + border-bottom: 2px solid transparent; + cursor: pointer; + font-size: 0.95rem; + color: var(--tab-color, #666); + transition: all 0.2s; +} + +:global(.dark) .tabButton { + --tab-color: #aaa; +} + +.tabButton:hover { + color: var(--tab-hover-color, #1890ff); +} + +:global(.dark) .tabButton:hover { + --tab-hover-color: #40a9ff; +} + +.tabButton.activeTab { + color: var(--active-tab-color, #1890ff); + border-bottom: 2px solid var(--active-tab-border, #1890ff); font-weight: 500; } -.selectedUser:hover { - background-color: #d8e5fd; +:global(.dark) .tabButton.activeTab { + --active-tab-color: #1890ff; + --active-tab-border: #1890ff; } -:global(.dark) .selectedUser { - background-color: #1a3a6c; +.userListManage { + list-style: none; + padding: 0; + margin: 0; + max-height: 200px; + overflow-y: auto; + border: 1px solid var(--list-border, #d9d9d9); + border-radius: 4px; } -:global(.dark) .selectedUser:hover { - background-color: #254b85; +:global(.dark) .userListManage { + --list-border: #444; } -.disabledItem { +.userListItemManage { + padding: 0.75rem; + border-bottom: 1px solid var(--item-border, #f0f0f0); + background-color: var(--item-bg, #fff); + color: var(--item-color, #333); + display: flex; + justify-content: space-between; + align-items: center; +} + +:global(.dark) .userListItemManage { + --item-border: #333; + --item-bg: #2a2a2a; + --item-color: #e0e0e0; +} + +.userListItemManage:last-child { + border-bottom: none; +} + +.userInfo { + flex: 1; +} + +.unshareButton { + background: none; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0.4rem; + border-radius: 4px; + color: var(--unshare-color, #666); + transition: all 0.2s; +} + +:global(.dark) .unshareButton { + --unshare-color: #aaa; +} + +.unshareButton:hover { + background-color: var(--unshare-hover-bg, rgba(255, 77, 79, 0.1)); + color: var(--unshare-hover-color, #ff4d4f); +} + +:global(.dark) .unshareButton:hover { + --unshare-hover-bg: rgba(255, 77, 79, 0.2); + --unshare-hover-color: #ff7875; +} + +.unshareButton.disabledButton { opacity: 0.6; cursor: not-allowed; } -.disabledItem:hover { - background-color: inherit; +@media (max-width: 768px) { + .userListShare, .userListManage { + max-height: 150px; + } + + .tabButton { + padding: 0.6rem 1rem; + font-size: 0.9rem; + } } -:global(.dark) .disabledItem:hover { - background-color: inherit; -} - -/* Estilo para botones deshabilitados */ -.modalButtonConfirm:disabled, -.modalButtonCancel:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.modalButtonConfirm:disabled:hover, -.modalButtonCancel:disabled:hover { - background-color: inherit; - border-color: inherit; -} - -:global(.dark) .modalButtonConfirm:disabled:hover, -:global(.dark) .modalButtonCancel:disabled:hover { - background-color: inherit; - border-color: inherit; +@media (max-width: 480px) { + .userListItemManage { + padding: 0.6rem; + font-size: 0.9rem; + } + + .tabButton { + padding: 0.5rem 0.75rem; + font-size: 0.85rem; + } } \ No newline at end of file diff --git a/src/components/DrawingCard/DrawingCard.tsx b/src/components/DrawingCard/DrawingCard.tsx index bee63c1..3b82f4a 100644 --- a/src/components/DrawingCard/DrawingCard.tsx +++ b/src/components/DrawingCard/DrawingCard.tsx @@ -8,6 +8,7 @@ import { deleteDrawing as apiDeleteDrawing, getUsers as apiGetUsers, shareDrawingWithUser as apiShareDrawing, + unshareDrawingWithUser as apiUnshareDrawing, } from '@/lib/api'; import { IUser } from '@/models/User'; import styles from './DrawingCard.module.css'; @@ -42,11 +43,14 @@ export default function DrawingCard({ // States for the share modal const [showShareModal, setShowShareModal] = useState(false); const [usersForSharing, setUsersForSharing] = useState([]); + const [sharedWithUsers, setSharedWithUsers] = useState([]); const [shareSearchTerm, setShareSearchTerm] = useState(''); const [selectedUserIdToShare, setSelectedUserIdToShare] = useState(null); const [shareError, setShareError] = useState(null); const [isLoadingUsers, setIsLoadingUsers] = useState(false); const [isSharing, setIsSharing] = useState(false); + const [isUnsharing, setIsUnsharing] = useState(false); + const [activeTab, setActiveTab] = useState<'share' | 'manage'>('share'); const handleCancelEdit = useCallback(() => { setIsEditing(false); @@ -164,19 +168,37 @@ export default function DrawingCard({ setShareError(null); setSelectedUserIdToShare(null); setShareSearchTerm(''); - if (usersForSharing.length === 0) { // Load users only if they haven't been loaded before - setIsLoadingUsers(true); + setActiveTab('share'); + setIsLoadingUsers(true); + + try { const response = await apiGetUsers(); - setIsLoadingUsers(false); if (response.data) { - // Filter out the owner and already shared users - const filteredUsers = response.data.filter( - (user) => user._id !== drawing.owner_id && !drawing.shared_with.includes(user._id as string) + // Get all users + const allUsers = response.data; + + // Filter users that already have access + const sharedWithUserIds = drawing.shared_with || []; + const usersWithAccess = allUsers.filter(user => + sharedWithUserIds.includes(user._id as string) ); - setUsersForSharing(filteredUsers); + + // Filter users that don't have access yet (excluding the owner) + const usersWithoutAccess = allUsers.filter(user => + user._id !== drawing.owner_id && + !sharedWithUserIds.includes(user._id as string) + ); + + setSharedWithUsers(usersWithAccess); + setUsersForSharing(usersWithoutAccess); } else { setShareError(response.error || t('drawingCard.errors.loadUsersFailed') || "Failed to load users."); } + } catch (err) { + setShareError(t('drawingCard.errors.loadUsersFailed') || "Failed to load users."); + console.error("Load users error:", err); + } finally { + setIsLoadingUsers(false); } }; @@ -194,17 +216,61 @@ export default function DrawingCard({ } setIsSharing(true); setShareError(null); - const response = await apiShareDrawing(drawing._id, selectedUserIdToShare); - setIsSharing(false); - if (response.data) { - setShowShareModal(false); - if (onDrawingShared) { - onDrawingShared(response.data); + + try { + const response = await apiShareDrawing(drawing._id, selectedUserIdToShare); + if (response.data) { + // Find the user that was shared with + const sharedUser = usersForSharing.find(user => user._id === selectedUserIdToShare); + if (sharedUser) { + // Add to shared users list + setSharedWithUsers(prev => [...prev, sharedUser]); + // Remove from available users list + setUsersForSharing(prev => prev.filter(user => user._id !== selectedUserIdToShare)); + } + + if (onDrawingShared) { + onDrawingShared(response.data); + } + setSelectedUserIdToShare(null); + } else { + setShareError(response.error || t('drawingCard.errors.shareFailed') || "Failed to share drawing."); } - setUsersForSharing(prevUsers => prevUsers.filter(u => u._id !== selectedUserIdToShare)); - setSelectedUserIdToShare(null); - } else { - setShareError(response.error || t('drawingCard.errors.shareFailed') || "Failed to share drawing."); + } catch (err) { + setShareError(t('drawingCard.errors.shareFailed') || "Failed to share drawing."); + console.error("Share error:", err); + } finally { + setIsSharing(false); + } + }; + + const handleUnshareDrawing = async (userId: string) => { + setIsUnsharing(true); + setShareError(null); + + try { + const response = await apiUnshareDrawing(drawing._id, userId); + if (response.data) { + // Find the user that was unshared + const unsharedUser = sharedWithUsers.find(user => user._id === userId); + if (unsharedUser) { + // Remove from shared users list + setSharedWithUsers(prev => prev.filter(user => user._id !== userId)); + // Add to available users list + setUsersForSharing(prev => [...prev, unsharedUser]); + } + + if (onDrawingShared) { + onDrawingShared(response.data); + } + } else { + setShareError(response.error || t('drawingCard.errors.unshareFailed') || "Failed to unshare drawing."); + } + } catch (err) { + setShareError(t('drawingCard.errors.unshareFailed') || "Failed to unshare drawing."); + console.error("Unshare error:", err); + } finally { + setIsUnsharing(false); } }; @@ -213,6 +279,11 @@ export default function DrawingCard({ user.email.toLowerCase().includes(shareSearchTerm.toLowerCase()) ); + const filteredSharedWithUsers = sharedWithUsers.filter((user) => + user.username.toLowerCase().includes(shareSearchTerm.toLowerCase()) || + user.email.toLowerCase().includes(shareSearchTerm.toLowerCase()) + ); + const modalTitle = i18nCardIsLoading ? "Confirm Deletion" : t('drawingCard.confirmDelete.title') || "Confirm Deletion"; @@ -251,6 +322,15 @@ export default function DrawingCard({ const shareModalNoUsersFoundText = i18nCardIsLoading ? "No users available to share with or match your search." : t('drawingCard.shareModal.noUsersFound') || "No users available to share with or match your search."; + const manageSharesTabText = i18nCardIsLoading + ? "Manage Shares" + : t('drawingCard.shareModal.manageSharesTab') || "Manage Shares"; + const shareTabText = i18nCardIsLoading + ? "Share" + : t('drawingCard.shareModal.shareTab') || "Share"; + const noSharedUsersText = i18nCardIsLoading + ? "This drawing is not shared with anyone yet." + : t('drawingCard.shareModal.noSharedUsers') || "This drawing is not shared with anyone yet."; return ( <> @@ -322,50 +402,101 @@ export default function DrawingCard({ title={shareModalTitle} >
+
+ + +
+ setShareSearchTerm(e.target.value)} className={styles.shareSearchInput} - disabled={isLoadingUsers || isSharing} + disabled={isLoadingUsers || isSharing || isUnsharing} /> + {isLoadingUsers &&

{t('drawingCard.shareModal.loadingUsers') || 'Loading users...'}

} {shareError &&

{shareError}

} - {!isLoadingUsers && filteredUsersForSharing.length === 0 && ( -

{shareModalNoUsersFoundText}

- )} + {activeTab === 'share' && !isLoadingUsers && ( + <> + {filteredUsersForSharing.length === 0 && ( +

{shareModalNoUsersFoundText}

+ )} - {!isLoadingUsers && filteredUsersForSharing.length > 0 && ( -
    - {filteredUsersForSharing.map((user) => ( -
  • !isSharing && setSelectedUserIdToShare(user._id as string)} - className={`${styles.userListItemShare} ${selectedUserIdToShare === user._id ? styles.selectedUser : ''} ${isSharing ? styles.disabledItem : ''}`} - > - {user.username} ({user.email}) -
  • - ))} -
+ {filteredUsersForSharing.length > 0 && ( +
    + {filteredUsersForSharing.map((user) => ( +
  • !isSharing && setSelectedUserIdToShare(user._id as string)} + className={`${styles.userListItemShare} ${selectedUserIdToShare === user._id ? styles.selectedUser : ''} ${isSharing ? styles.disabledItem : ''}`} + > + {user.username} ({user.email}) +
  • + ))} +
+ )} + + )} + + {activeTab === 'manage' && !isLoadingUsers && ( + <> + {filteredSharedWithUsers.length === 0 && ( +

{noSharedUsersText}

+ )} + + {filteredSharedWithUsers.length > 0 && ( +
    + {filteredSharedWithUsers.map((user) => ( +
  • + + {user.username} ({user.email}) + + +
  • + ))} +
+ )} + )}
+
- + + {activeTab === 'share' && ( + + )}