mirror of
https://github.com/wekan/wekan.git
synced 2026-01-01 07:08:49 +01:00
Drag any files from file manager to minicard or opened card. Part 2.
Thanks to xet7 ! Fixes #2936
This commit is contained in:
parent
c1cbcdcc72
commit
cdd7d69c66
8 changed files with 404 additions and 6 deletions
|
|
@ -106,6 +106,142 @@
|
|||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
/* Upload progress indicators for drag-and-drop uploads */
|
||||
.minicard-upload-progress,
|
||||
.card-details-upload-progress {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
margin: 8px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.upload-progress-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.upload-progress-header i {
|
||||
margin-right: 8px;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.upload-progress-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
padding: 8px;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.upload-progress-item.upload-error {
|
||||
border-color: #dc3545;
|
||||
background: #f8d7da;
|
||||
}
|
||||
|
||||
.upload-progress-filename {
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
color: #495057;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.upload-progress-bar {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: #e9ecef;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.upload-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #007bff, #0056b3);
|
||||
transition: width 0.3s ease;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.upload-progress-item.upload-error .upload-progress-fill {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.upload-progress-error,
|
||||
.upload-progress-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.upload-progress-error {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.upload-progress-success {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.upload-progress-error i,
|
||||
.upload-progress-success i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* Minicard specific styles */
|
||||
.minicard-upload-progress {
|
||||
margin: 4px 0;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.minicard-upload-progress .upload-progress-item {
|
||||
padding: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.minicard-upload-progress .upload-progress-filename {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Card details specific styles */
|
||||
.card-details-upload-progress {
|
||||
margin: 12px 0;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-details-upload-progress .upload-progress-header {
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-details-upload-progress .upload-progress-item {
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-details-upload-progress .upload-progress-filename {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Drag over state for minicards */
|
||||
.minicard.is-dragging-over {
|
||||
border: 2px dashed #007bff !important;
|
||||
background: rgba(0, 123, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Drag over state for card details */
|
||||
.js-card-details.is-dragging-over {
|
||||
border: 2px dashed #007bff !important;
|
||||
background: rgba(0, 123, 255, 0.05) !important;
|
||||
}
|
||||
top: 0;
|
||||
right: 8px;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { ObjectID } from 'bson';
|
||||
import DOMPurify from 'dompurify';
|
||||
import uploadProgressManager from '/client/lib/uploadProgressManager';
|
||||
|
||||
const filesize = require('filesize');
|
||||
const prettyMilliseconds = require('pretty-ms');
|
||||
|
|
@ -333,13 +334,17 @@ export function handleFileUpload(card, files) {
|
|||
// Check if board allows attachments
|
||||
const board = card.board();
|
||||
if (!board || !board.allowsAttachments) {
|
||||
console.warn('Attachments not allowed on this board');
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Attachments not allowed on this board');
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check if user can modify the card
|
||||
if (!card.canModifyCard()) {
|
||||
console.warn('User does not have permission to modify this card');
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('User does not have permission to modify this card');
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +353,9 @@ export function handleFileUpload(card, files) {
|
|||
for (const file of files) {
|
||||
// Basic file validation
|
||||
if (!file || !file.name) {
|
||||
console.warn('Invalid file object');
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Invalid file object');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -381,24 +388,36 @@ export function handleFileUpload(card, files) {
|
|||
false,
|
||||
);
|
||||
|
||||
// Add to progress manager for tracking
|
||||
const uploadId = uploadProgressManager.addUpload(card._id, uploader, file);
|
||||
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
if (fileRef.isImage) {
|
||||
card.setCover(fileRef._id);
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(`Set cover image for card ${card._id}: ${fileRef.name}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('Upload error:', error);
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.error('Upload error:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uploader.on('error', (error) => {
|
||||
console.error('Upload error:', error);
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.error('Upload error:', error);
|
||||
}
|
||||
});
|
||||
|
||||
uploads.push(uploader);
|
||||
uploader.start();
|
||||
} catch (error) {
|
||||
console.error('Failed to create uploader:', error);
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.error('Failed to create uploader:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,26 @@ template(name="cardDetails")
|
|||
else
|
||||
p.warning {{_ 'card-archived'}}
|
||||
|
||||
// Upload progress indicator for drag-and-drop uploads
|
||||
if hasActiveUploads
|
||||
.card-details-upload-progress
|
||||
.upload-progress-header
|
||||
i.fa.fa-upload
|
||||
span {{_ 'uploading-files'}} ({{uploadCount}})
|
||||
each uploads
|
||||
.upload-progress-item(class="{{#if $eq status 'error'}}upload-error{{/if}}")
|
||||
.upload-progress-filename {{file.name}}
|
||||
.upload-progress-bar
|
||||
.upload-progress-fill(style="width: {{progress}}%")
|
||||
if $eq status 'error'
|
||||
.upload-progress-error
|
||||
i.fa.fa-exclamation-triangle
|
||||
span {{_ 'upload-failed'}}
|
||||
else if $eq status 'completed'
|
||||
.upload-progress-success
|
||||
i.fa.fa-check
|
||||
span {{_ 'upload-completed'}}
|
||||
|
||||
.card-details-left
|
||||
|
||||
.card-details-items
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { ALLOWED_COLORS } from '/config/const';
|
|||
import { UserAvatar } from '../users/userAvatar';
|
||||
import { DialogWithBoardSwimlaneList } from '/client/lib/dialogWithBoardSwimlaneList';
|
||||
import { handleFileUpload } from './attachments';
|
||||
import uploadProgressManager from '/client/lib/uploadProgressManager';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData } = Utils;
|
||||
|
|
@ -544,6 +545,16 @@ Template.cardDetails.helpers({
|
|||
isPopup() {
|
||||
let ret = !!Utils.getPopupCardId();
|
||||
return ret;
|
||||
},
|
||||
// Upload progress helpers
|
||||
hasActiveUploads() {
|
||||
return uploadProgressManager.hasActiveUploads(this._id);
|
||||
},
|
||||
uploads() {
|
||||
return uploadProgressManager.getUploadsForCard(this._id);
|
||||
},
|
||||
uploadCount() {
|
||||
return uploadProgressManager.getUploadCountForCard(this._id);
|
||||
}
|
||||
});
|
||||
Template.cardDetailsPopup.onDestroyed(() => {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,27 @@ template(name="minicard")
|
|||
if cover
|
||||
if currentBoard.allowsCoverAttachmentOnMinicard
|
||||
.minicard-cover(style="background-image: url('{{cover.link 'original'}}?dummyReloadAfterSessionEstablished={{sess}}');")
|
||||
|
||||
// Upload progress indicator for drag-and-drop uploads
|
||||
if hasActiveUploads
|
||||
.minicard-upload-progress
|
||||
.upload-progress-header
|
||||
i.fa.fa-upload
|
||||
span {{_ 'uploading-files'}} ({{uploadCount}})
|
||||
each uploads
|
||||
.upload-progress-item(class="{{#if $eq status 'error'}}upload-error{{/if}}")
|
||||
.upload-progress-filename {{file.name}}
|
||||
.upload-progress-bar
|
||||
.upload-progress-fill(style="width: {{progress}}%")
|
||||
if $eq status 'error'
|
||||
.upload-progress-error
|
||||
i.fa.fa-exclamation-triangle
|
||||
span {{_ 'upload-failed'}}
|
||||
else if $eq status 'completed'
|
||||
.upload-progress-success
|
||||
i.fa.fa-check
|
||||
span {{_ 'upload-completed'}}
|
||||
|
||||
.minicard-title
|
||||
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ReactiveCache } from '/imports/reactiveCache';
|
|||
import { TAPi18n } from '/imports/i18n';
|
||||
import { CustomFieldStringTemplate } from '/client/lib/customFields';
|
||||
import { handleFileUpload } from './attachments';
|
||||
import uploadProgressManager from '/client/lib/uploadProgressManager';
|
||||
|
||||
// Template.cards.events({
|
||||
// 'click .member': Popup.open('cardMember')
|
||||
|
|
@ -185,6 +186,16 @@ Template.minicard.helpers({
|
|||
},
|
||||
isWatching() {
|
||||
return this.findWatcher(Meteor.userId());
|
||||
},
|
||||
// Upload progress helpers
|
||||
hasActiveUploads() {
|
||||
return uploadProgressManager.hasActiveUploads(this._id);
|
||||
},
|
||||
uploads() {
|
||||
return uploadProgressManager.getUploadsForCard(this._id);
|
||||
},
|
||||
uploadCount() {
|
||||
return uploadProgressManager.getUploadCountForCard(this._id);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue