SAVE
This commit is contained in:
parent
3aa27efa36
commit
2e59823caa
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
</>
|
||||
|
|
Loading…
Reference in New Issue
Block a user