This commit is contained in:
borrageiros 2025-05-14 21:07:48 +02:00
parent 3aa27efa36
commit 2e59823caa
4 changed files with 528 additions and 316 deletions

View File

@ -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": {

View File

@ -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": {

View File

@ -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;
}
}

View File

@ -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<IUser[]>([]);
const [sharedWithUsers, setSharedWithUsers] = useState<IUser[]>([]);
const [shareSearchTerm, setShareSearchTerm] = useState('');
const [selectedUserIdToShare, setSelectedUserIdToShare] = useState<string | null>(null);
const [shareError, setShareError] = useState<string | null>(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}
>
<div className={styles.shareModalContent}>
<div className={styles.tabsContainer}>
<button
className={`${styles.tabButton} ${activeTab === 'share' ? styles.activeTab : ''}`}
onClick={() => setActiveTab('share')}
>
{shareTabText}
</button>
<button
className={`${styles.tabButton} ${activeTab === 'manage' ? styles.activeTab : ''}`}
onClick={() => setActiveTab('manage')}
>
{manageSharesTabText}
</button>
</div>
<input
type="text"
placeholder={shareModalSearchPlaceholder}
value={shareSearchTerm}
onChange={(e) => setShareSearchTerm(e.target.value)}
className={styles.shareSearchInput}
disabled={isLoadingUsers || isSharing}
disabled={isLoadingUsers || isSharing || isUnsharing}
/>
{isLoadingUsers && <p>{t('drawingCard.shareModal.loadingUsers') || 'Loading users...'}</p>}
{shareError && <p className={styles.modalErrorText}>{shareError}</p>}
{!isLoadingUsers && filteredUsersForSharing.length === 0 && (
<p>{shareModalNoUsersFoundText}</p>
)}
{activeTab === 'share' && !isLoadingUsers && (
<>
{filteredUsersForSharing.length === 0 && (
<p>{shareModalNoUsersFoundText}</p>
)}
{!isLoadingUsers && filteredUsersForSharing.length > 0 && (
<ul className={styles.userListShare}>
{filteredUsersForSharing.map((user) => (
<li
key={user._id as string}
onClick={() => !isSharing && setSelectedUserIdToShare(user._id as string)}
className={`${styles.userListItemShare} ${selectedUserIdToShare === user._id ? styles.selectedUser : ''} ${isSharing ? styles.disabledItem : ''}`}
>
{user.username} ({user.email})
</li>
))}
</ul>
{filteredUsersForSharing.length > 0 && (
<ul className={styles.userListShare}>
{filteredUsersForSharing.map((user) => (
<li
key={user._id as string}
onClick={() => !isSharing && setSelectedUserIdToShare(user._id as string)}
className={`${styles.userListItemShare} ${selectedUserIdToShare === user._id ? styles.selectedUser : ''} ${isSharing ? styles.disabledItem : ''}`}
>
{user.username} ({user.email})
</li>
))}
</ul>
)}
</>
)}
{activeTab === 'manage' && !isLoadingUsers && (
<>
{filteredSharedWithUsers.length === 0 && (
<p>{noSharedUsersText}</p>
)}
{filteredSharedWithUsers.length > 0 && (
<ul className={styles.userListManage}>
{filteredSharedWithUsers.map((user) => (
<li key={user._id as string} className={styles.userListItemManage}>
<span className={styles.userInfo}>
{user.username} ({user.email})
</span>
<button
onClick={() => !isUnsharing && handleUnshareDrawing(user._id as string)}
className={`${styles.unshareButton} ${isUnsharing ? styles.disabledButton : ''}`}
disabled={isUnsharing}
>
<Icon name="user-x" size={16} />
</button>
</li>
))}
</ul>
)}
</>
)}
</div>
<div className={styles.modalFooter}>
<button
onClick={handleCloseShareModal}
className={styles.modalButtonCancel}
disabled={isSharing}
disabled={isSharing || isUnsharing}
>
{shareModalCancelButtonText}
</button>
<button
onClick={handleConfirmShare}
className={styles.modalButtonConfirm}
disabled={!selectedUserIdToShare || isLoadingUsers || isSharing}
>
{isSharing ? (t('drawingCard.shareModal.sharingButton') || 'Sharing...') : shareModalConfirmButtonText}
</button>
{activeTab === 'share' && (
<button
onClick={handleConfirmShare}
className={styles.modalButtonConfirm}
disabled={!selectedUserIdToShare || isLoadingUsers || isSharing}
>
{isSharing ? (t('drawingCard.shareModal.sharingButton') || 'Sharing...') : shareModalConfirmButtonText}
</button>
)}
</div>
</Modal>
</>