mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Added Date Format setting to Opened Card.
Thanks to xet7 ! Fixes #2011, fixes #1176
This commit is contained in:
parent
516552cce6
commit
2dd3916f7e
8 changed files with 161 additions and 13 deletions
|
|
@ -1,8 +1,10 @@
|
||||||
import { TAPi18n } from '/imports/i18n';
|
import { TAPi18n } from '/imports/i18n';
|
||||||
import { DatePicker } from '/client/lib/datepicker';
|
import { DatePicker } from '/client/lib/datepicker';
|
||||||
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
import {
|
import {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDate,
|
formatDate,
|
||||||
|
formatDateByUserPreference,
|
||||||
formatTime,
|
formatTime,
|
||||||
getISOWeek,
|
getISOWeek,
|
||||||
isValidDate,
|
isValidDate,
|
||||||
|
|
@ -177,7 +179,9 @@ CardCustomField.register('cardCustomField');
|
||||||
}
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return calendar(this.date.get());
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
showISODate() {
|
showISODate() {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { DatePicker } from '/client/lib/datepicker';
|
||||||
import {
|
import {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDate,
|
formatDate,
|
||||||
|
formatDateByUserPreference,
|
||||||
formatTime,
|
formatTime,
|
||||||
getISOWeek,
|
getISOWeek,
|
||||||
isValidDate,
|
isValidDate,
|
||||||
|
|
@ -131,7 +132,9 @@ const CardDate = BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return calendar(this.date.get());
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
showISODate() {
|
showISODate() {
|
||||||
|
|
@ -166,7 +169,10 @@ class CardReceivedDate extends CardDate {
|
||||||
}
|
}
|
||||||
|
|
||||||
showTitle() {
|
showTitle() {
|
||||||
return `${TAPi18n.__('card-received-on')} ${format(this.date.get(), 'LLLL')}`;
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
|
return `${TAPi18n.__('card-received-on')} ${formattedDate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
|
|
@ -201,7 +207,10 @@ class CardStartDate extends CardDate {
|
||||||
}
|
}
|
||||||
|
|
||||||
showTitle() {
|
showTitle() {
|
||||||
return `${TAPi18n.__('card-start-on')} ${format(this.date.get(), 'LLLL')}`;
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
|
return `${TAPi18n.__('card-start-on')} ${formattedDate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
|
|
@ -237,7 +246,10 @@ class CardDueDate extends CardDate {
|
||||||
}
|
}
|
||||||
|
|
||||||
showTitle() {
|
showTitle() {
|
||||||
return `${TAPi18n.__('card-due-on')} ${format(this.date.get(), 'LLLL')}`;
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
|
return `${TAPi18n.__('card-due-on')} ${formattedDate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
|
|
@ -315,7 +327,10 @@ class CardCustomFieldDate extends CardDate {
|
||||||
}
|
}
|
||||||
|
|
||||||
showTitle() {
|
showTitle() {
|
||||||
return `${format(this.date.get(), 'LLLL')}`;
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
const formattedDate = formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
|
return `${formattedDate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
classes() {
|
classes() {
|
||||||
|
|
@ -334,7 +349,9 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
||||||
}
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'L');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
}.register('minicardReceivedDate'));
|
}.register('minicardReceivedDate'));
|
||||||
|
|
||||||
|
|
@ -344,7 +361,9 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
||||||
}
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'YYYY-MM-DD HH:mm');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
}.register('minicardStartDate'));
|
}.register('minicardStartDate'));
|
||||||
|
|
||||||
|
|
@ -354,7 +373,9 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
||||||
}
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'YYYY-MM-DD HH:mm');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
}.register('minicardDueDate'));
|
}.register('minicardDueDate'));
|
||||||
|
|
||||||
|
|
@ -364,7 +385,9 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
||||||
}
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'YYYY-MM-DD HH:mm');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
}.register('minicardEndDate'));
|
}.register('minicardEndDate'));
|
||||||
|
|
||||||
|
|
@ -374,7 +397,9 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
||||||
}
|
}
|
||||||
|
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'L');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
}.register('minicardCustomFieldDate'));
|
}.register('minicardCustomFieldDate'));
|
||||||
|
|
||||||
|
|
@ -391,7 +416,9 @@ class VoteEndDate extends CardDate {
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'L') + ' ' + format(this.date.get(), 'HH:mm');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
showTitle() {
|
showTitle() {
|
||||||
return `${TAPi18n.__('card-end-on')} ${this.date.get().toLocaleString()}`;
|
return `${TAPi18n.__('card-end-on')} ${this.date.get().toLocaleString()}`;
|
||||||
|
|
@ -418,7 +445,9 @@ class PokerEndDate extends CardDate {
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
showDate() {
|
showDate() {
|
||||||
return format(this.date.get(), 'l LT');
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
const dateFormat = currentUser ? currentUser.getDateFormat() : 'YYYY-MM-DD';
|
||||||
|
return formatDateByUserPreference(this.date.get(), dateFormat, true);
|
||||||
}
|
}
|
||||||
showTitle() {
|
showTitle() {
|
||||||
return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`;
|
return `${TAPi18n.__('card-end-on')} ${format(this.date.get(), 'LLLL')}`;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,31 @@
|
||||||
|
/* Date Format Selector */
|
||||||
|
.card-details-item-date-format {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-details-item-date-format .card-details-item-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-details-item-date-format .js-date-format-selector {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-details-item-date-format .js-date-format-selector:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #007cba;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.assignee {
|
.assignee {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,16 @@ template(name="cardDetails")
|
||||||
if currentBoard.hasAnyAllowsDate
|
if currentBoard.hasAnyAllowsDate
|
||||||
hr
|
hr
|
||||||
|
|
||||||
|
.card-details-item.card-details-item-date-format
|
||||||
|
h3.card-details-item-title
|
||||||
|
| 📅
|
||||||
|
| {{_ 'date-format'}}
|
||||||
|
.card-details-item-content
|
||||||
|
select.js-date-format-selector
|
||||||
|
option(value="YYYY-MM-DD" selected="{{#if isDateFormat 'YYYY-MM-DD'}}selected{{/if}}") {{_ 'date-format-yyyy-mm-dd'}}
|
||||||
|
option(value="DD-MM-YYYY" selected="{{#if isDateFormat 'DD-MM-YYYY'}}selected{{/if}}") {{_ 'date-format-dd-mm-yyyy'}}
|
||||||
|
option(value="MM-DD-YYYY" selected="{{#if isDateFormat 'MM-DD-YYYY'}}selected{{/if}}") {{_ 'date-format-mm-dd-yyyy'}}
|
||||||
|
|
||||||
if currentBoard.allowsReceivedDate
|
if currentBoard.allowsReceivedDate
|
||||||
.card-details-item.card-details-item-received
|
.card-details-item.card-details-item-received
|
||||||
h3.card-details-item-title
|
h3.card-details-item-title
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,10 @@ BlazeComponent.extendComponent({
|
||||||
const $tooltip = this.$('.card-details-header .copied-tooltip');
|
const $tooltip = this.$('.card-details-header .copied-tooltip');
|
||||||
Utils.showCopied(promise, $tooltip);
|
Utils.showCopied(promise, $tooltip);
|
||||||
},
|
},
|
||||||
|
'change .js-date-format-selector'(event) {
|
||||||
|
const dateFormat = event.target.value;
|
||||||
|
Meteor.call('changeDateFormat', dateFormat);
|
||||||
|
},
|
||||||
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
|
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
|
||||||
'submit .js-card-description'(event) {
|
'submit .js-card-description'(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
@ -568,6 +572,11 @@ Template.cardDetails.helpers({
|
||||||
let ret = !!Utils.getPopupCardId();
|
let ret = !!Utils.getPopupCardId();
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
isDateFormat(format) {
|
||||||
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
if (!currentUser) return format === 'YYYY-MM-DD';
|
||||||
|
return currentUser.getDateFormat() === format;
|
||||||
|
},
|
||||||
// Upload progress helpers
|
// Upload progress helpers
|
||||||
hasActiveUploads() {
|
hasActiveUploads() {
|
||||||
return uploadProgressManager.hasActiveUploads(this._id);
|
return uploadProgressManager.hasActiveUploads(this._id);
|
||||||
|
|
|
||||||
|
|
@ -354,6 +354,10 @@
|
||||||
"custom-field-text": "Text",
|
"custom-field-text": "Text",
|
||||||
"custom-fields": "Custom Fields",
|
"custom-fields": "Custom Fields",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
|
"date-format": "Date Format",
|
||||||
|
"date-format-yyyy-mm-dd": "YYYY-MM-DD HH:MM",
|
||||||
|
"date-format-dd-mm-yyyy": "DD-MM-YYYY HH:MM",
|
||||||
|
"date-format-mm-dd-yyyy": "MM-DD-YYYY HH:MM",
|
||||||
"decline": "Decline",
|
"decline": "Decline",
|
||||||
"default-avatar": "Default avatar",
|
"default-avatar": "Default avatar",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,44 @@ export function formatDate(date) {
|
||||||
return `${year}-${month}-${day}`;
|
return `${year}-${month}-${day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date according to user's preferred format
|
||||||
|
* @param {Date|string} date - Date to format
|
||||||
|
* @param {string} format - Format string (YYYY-MM-DD, DD-MM-YYYY, MM-DD-YYYY)
|
||||||
|
* @param {boolean} includeTime - Whether to include time (HH:MM)
|
||||||
|
* @returns {string} Formatted date string
|
||||||
|
*/
|
||||||
|
export function formatDateByUserPreference(date, format = 'YYYY-MM-DD', includeTime = true) {
|
||||||
|
const d = new Date(date);
|
||||||
|
if (isNaN(d.getTime())) return '';
|
||||||
|
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(d.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(d.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(d.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
let dateString;
|
||||||
|
switch (format) {
|
||||||
|
case 'DD-MM-YYYY':
|
||||||
|
dateString = `${day}-${month}-${year}`;
|
||||||
|
break;
|
||||||
|
case 'MM-DD-YYYY':
|
||||||
|
dateString = `${month}-${day}-${year}`;
|
||||||
|
break;
|
||||||
|
case 'YYYY-MM-DD':
|
||||||
|
default:
|
||||||
|
dateString = `${year}-${month}-${day}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeTime) {
|
||||||
|
return `${dateString} ${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a time to HH:mm format
|
* Format a time to HH:mm format
|
||||||
* @param {Date|string} date - Date to format
|
* @param {Date|string} date - Date to format
|
||||||
|
|
|
||||||
|
|
@ -465,6 +465,15 @@ Users.attachSchema(
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
},
|
},
|
||||||
|
'profile.dateFormat': {
|
||||||
|
/**
|
||||||
|
* User-specified date format for displaying dates (includes time HH:MM).
|
||||||
|
*/
|
||||||
|
type: String,
|
||||||
|
optional: true,
|
||||||
|
allowedValues: ['YYYY-MM-DD', 'DD-MM-YYYY', 'MM-DD-YYYY'],
|
||||||
|
defaultValue: 'YYYY-MM-DD',
|
||||||
|
},
|
||||||
'profile.zoomLevel': {
|
'profile.zoomLevel': {
|
||||||
/**
|
/**
|
||||||
* User-specified zoom level for board view (1.0 = 100%, 1.5 = 150%, etc.)
|
* User-specified zoom level for board view (1.0 = 100%, 1.5 = 150%, etc.)
|
||||||
|
|
@ -1049,6 +1058,11 @@ Users.helpers({
|
||||||
return profile.startDayOfWeek;
|
return profile.startDayOfWeek;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDateFormat() {
|
||||||
|
const profile = this.profile || {};
|
||||||
|
return profile.dateFormat || 'YYYY-MM-DD';
|
||||||
|
},
|
||||||
|
|
||||||
getTemplatesBoardId() {
|
getTemplatesBoardId() {
|
||||||
return (this.profile || {}).templatesBoardId;
|
return (this.profile || {}).templatesBoardId;
|
||||||
},
|
},
|
||||||
|
|
@ -1452,6 +1466,14 @@ Users.mutations({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setDateFormat(dateFormat) {
|
||||||
|
return {
|
||||||
|
$set: {
|
||||||
|
'profile.dateFormat': dateFormat,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
setBoardView(view) {
|
setBoardView(view) {
|
||||||
return {
|
return {
|
||||||
$set: {
|
$set: {
|
||||||
|
|
@ -1597,6 +1619,10 @@ Meteor.methods({
|
||||||
check(startDay, Number);
|
check(startDay, Number);
|
||||||
ReactiveCache.getCurrentUser().setStartDayOfWeek(startDay);
|
ReactiveCache.getCurrentUser().setStartDayOfWeek(startDay);
|
||||||
},
|
},
|
||||||
|
changeDateFormat(dateFormat) {
|
||||||
|
check(dateFormat, String);
|
||||||
|
ReactiveCache.getCurrentUser().setDateFormat(dateFormat);
|
||||||
|
},
|
||||||
applyListWidth(boardId, listId, width, constraint) {
|
applyListWidth(boardId, listId, width, constraint) {
|
||||||
check(boardId, String);
|
check(boardId, String);
|
||||||
check(listId, String);
|
check(listId, String);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue