Fiz/summaries (#15653)

* dailies monthly weeks of month summary

update monthly day-of-week scheduling summary, add scheduling summary to task form, and add 5th week warning

* formatting

* scheduling warning and summary UI updates

* fix(svg): use extant icon

---------

Co-authored-by: Hafiz <hafizbhamidi@gmail.com>
This commit is contained in:
Kalista Payne
2026-05-12 16:28:40 -05:00
committed by GitHub
parent c581b88213
commit dd413518f5
3 changed files with 166 additions and 24 deletions
@@ -412,6 +412,25 @@
</div>
</div>
</div>
<p
v-if="task.type === 'daily' && schedulingSummary"
class="scheduling-summary mt-2 mb-0"
>
{{ schedulingSummary }}
</p>
<div
v-if="task.type === 'daily' && schedulingWarning"
class="scheduling-warning mt-2"
>
<span
class="scheduling-warning-icon svg-icon color gray-50"
v-html="icons.alert"
></span>
<span
class="scheduling-warning-text"
v-html="schedulingWarning"
></span>
</div>
<div
v-if="!groupId"
class="tags-select option mt-3"
@@ -1109,6 +1128,42 @@
height: 1rem;
}
.scheduling-summary {
font-family: 'Roboto', sans-serif;
font-weight: 400;
font-style: normal;
font-size: 12px;
line-height: 16px;
color: $gray-50;
text-align: left;
}
.scheduling-warning {
display: flex;
align-items: flex-start;
font-family: 'Roboto', sans-serif;
font-weight: 400;
font-style: normal;
font-size: 12px;
line-height: 16px;
color: $gray-50;
}
.scheduling-warning-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
flex-shrink: 0;
margin-right: 6px;
margin-top: -1px;
}
.scheduling-warning-text {
flex: 1;
}
label {
display: inline-flex;
align-items: center;
@@ -1377,6 +1432,87 @@ export default {
}
return null;
},
schedulingSummary () {
if (!this.task || this.task.type !== 'daily') return '';
const { task } = this;
const everyXValue = +task.everyX;
let interval;
if (task.frequency === 'daily') {
interval = everyXValue === 1 ? this.$t('everyDay') : this.$t('everyXDays', { count: everyXValue });
} else if (task.frequency === 'weekly') {
interval = everyXValue === 1 ? this.$t('everyWeek') : this.$t('everyXWeeks', { count: everyXValue });
} else if (task.frequency === 'monthly') {
interval = everyXValue === 1 ? this.$t('everyMonth') : this.$t('everyXMonths', { count: everyXValue });
} else if (task.frequency === 'yearly') {
interval = everyXValue === 1 ? this.$t('everyYear') : this.$t('everyXYears', { count: everyXValue });
} else {
return '';
}
let details = '';
if (task.frequency === 'weekly') {
const dayNames = {
su: 'Sunday',
m: 'Monday',
t: 'Tuesday',
w: 'Wednesday',
th: 'Thursday',
f: 'Friday',
s: 'Saturday',
};
const activeDays = Object.keys(task.repeat || {}).filter(d => task.repeat[d]);
if (activeDays.length > 0) {
details = ` on ${activeDays.map(d => dayNames[d]).join(', ')}`;
}
} else if (task.frequency === 'monthly' && task.startDate) {
const dayOfMonth = moment(task.startDate).date();
if (task.weeksOfMonth && task.weeksOfMonth.length > 0) {
const weekNum = task.weeksOfMonth[0] + 1;
const weekStr = String(weekNum);
const lastDigit = weekStr.slice(-1);
let suffix = 'th';
if (lastDigit === '1' && weekStr !== '11') suffix = 'st';
if (lastDigit === '2' && weekStr !== '12') suffix = 'nd';
if (lastDigit === '3' && weekStr !== '13') suffix = 'rd';
const dayName = moment(task.startDate).format('dddd');
details = ` on the ${weekNum}${suffix} ${dayName} of the month`;
} else if (task.daysOfMonth && task.daysOfMonth.length > 0) {
const dom = task.daysOfMonth[0];
const domStr = String(dom);
const lastDigit = domStr.slice(-1);
let suffix = 'th';
if (lastDigit === '1' && domStr !== '11') suffix = 'st';
if (lastDigit === '2' && domStr !== '12') suffix = 'nd';
if (lastDigit === '3' && domStr !== '13') suffix = 'rd';
details = ` on the ${dom}${suffix}`;
} else {
const domStr = String(dayOfMonth);
const lastDigit = domStr.slice(-1);
let suffix = 'th';
if (lastDigit === '1' && domStr !== '11') suffix = 'st';
if (lastDigit === '2' && domStr !== '12') suffix = 'nd';
if (lastDigit === '3' && domStr !== '13') suffix = 'rd';
details = ` on the ${dayOfMonth}${suffix}`;
}
} else if (task.frequency === 'yearly' && task.startDate) {
details = ` on ${moment(task.startDate).format('MMMM Do')}`;
}
return `${this.$t('repeats')} ${interval}${details}`;
},
schedulingWarning () {
if (!this.task || this.task.type !== 'daily') return '';
const { task } = this;
if (task.frequency === 'monthly'
&& task.weeksOfMonth && task.weeksOfMonth.length > 0
&& task.weeksOfMonth[0] === 4
&& task.startDate) {
const dayName = moment(task.startDate).format('dddd');
return this.$t('fifthWeekWarning', { day: dayName });
}
return '';
},
repeatsOn: {
get () {
let repeatsOn = 'dayOfMonth';
@@ -222,14 +222,22 @@ export default {
return usernames;
},
summarySentence () {
let fifthWeekWarning = '';
if (this.task.type === 'daily' && this.task.frequency === 'monthly'
&& this.task.weeksOfMonth && this.task.weeksOfMonth.length > 0
&& this.task.weeksOfMonth[0] === 4) {
const activeDays = keys(pickBy(this.task.repeat, value => value === true));
const dayName = this.expandDayString[activeDays[0]];
fifthWeekWarning = ` ${this.$t('fifthWeekWarning', { day: dayName })}`;
}
if (this.task.type === 'daily' && moment().isBefore(this.task.startDate)) {
return `This is ${this.formattedDifficulty(this.task.priority)} task that will repeat
${this.formattedRepeatInterval(this.task.frequency, this.task.everyX)}${this.formattedDays(this.task.frequency, this.task.repeat, this.task.daysOfMonth, this.task.weeksOfMonth, this.task.startDate)}
starting on <strong>${moment(this.task.startDate).format('MM/DD/YYYY')}</strong>.`;
starting on <strong>${moment(this.task.startDate).format('MM/DD/YYYY')}</strong>.${fifthWeekWarning}`;
}
if (this.task.type === 'daily') {
return `This is ${this.formattedDifficulty(this.task.priority)} task that repeats
${this.formattedRepeatInterval(this.task.frequency, this.task.everyX)}${this.formattedDays(this.task.frequency, this.task.repeat, this.task.daysOfMonth, this.task.weeksOfMonth, this.task.startDate)}.`;
${this.formattedRepeatInterval(this.task.frequency, this.task.everyX)}${this.formattedDays(this.task.frequency, this.task.repeat, this.task.daysOfMonth, this.task.weeksOfMonth, this.task.startDate)}.${fifthWeekWarning}`;
}
if (this.task.date) {
return `This is ${this.formattedDifficulty(this.task.priority)} task that is due <strong>${moment(this.task.date).format('MM/DD/YYYY')}.`;
@@ -287,25 +295,14 @@ export default {
});
dayStringArray.push('</strong>');
} else if (weeksOfMonth.length > 0) {
switch (weeksOfMonth[0]) {
case 0:
dayStringArray.push('first');
break;
case 1:
dayStringArray.push('second');
break;
case 2:
dayStringArray.push('third');
break;
case 3:
dayStringArray.push('fourth');
break;
case 4:
dayStringArray.push('fifth');
break;
default:
break;
}
const weekNum = weeksOfMonth[0] + 1;
const weekNumStr = String(weekNum);
const lastDigit = weekNumStr.slice(-1);
let ordinalSuffix = 'th';
if (lastDigit === '1' && weekNumStr !== '11') ordinalSuffix = 'st';
if (lastDigit === '2' && weekNumStr !== '12') ordinalSuffix = 'nd';
if (lastDigit === '3' && weekNumStr !== '13') ordinalSuffix = 'rd';
dayStringArray.push(`${weekNum}${ordinalSuffix}`);
activeDays = keys(pickBy(repeat, value => value === true));
dayStringArray.push(` ${this.expandDayString[activeDays[0]]} of the month</strong>`);
}
@@ -343,9 +340,8 @@ export default {
if (numericX === 2) return '<strong>every other week</strong>';
return `<strong>every ${numericX} weeks</strong>`;
case 'monthly':
if (numericX === 1) return '<strong>every month</strong>';
if (numericX === 2) return '<strong>every other month</strong>';
return `<strong>every ${numericX} months</strong>`;
if (numericX === 1) return `<strong>${this.$t('everyMonth')}</strong>`;
return `<strong>${this.$t('everyXMonths', { count: numericX })}</strong>`;
case 'yearly':
if (numericX === 1) return '<strong>every year</strong>';
return `<strong>every ${everyX} years</strong>`;
+10
View File
@@ -123,6 +123,16 @@
"dayOfMonth": "Day of the Month",
"month": "Month",
"months": "Months",
"every": "every",
"everyDay": "every day",
"everyXDays": "every <%= count %> days",
"everyWeek": "every week",
"everyXWeeks": "every <%= count %> weeks",
"everyMonth": "every month",
"everyXMonths": "every <%= count %> months",
"everyYear": "every year",
"everyXYears": "every <%= count %> years",
"fifthWeekWarning": "This task <strong>will not</strong> appear due during months with fewer <%= day %>s",
"week": "Week",
"weeks": "Weeks",
"year": "Year",