From 38cdf756a5b456cd2817768adb330884a0c9ca76 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Thu, 23 Oct 2025 13:59:29 +0530 Subject: [PATCH 1/3] [WEB-5158] chore: calendar start of week alignment fix and code refactoring #7978 --- .../store/issue/issue_calendar_view.store.ts | 43 +++++++++++++++++-- apps/web/core/store/issue/root.store.ts | 2 +- packages/utils/src/calendar.ts | 20 +++++++-- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/apps/web/core/store/issue/issue_calendar_view.store.ts b/apps/web/core/store/issue/issue_calendar_view.store.ts index f9ee2c29d6..9e00cbd7eb 100644 --- a/apps/web/core/store/issue/issue_calendar_view.store.ts +++ b/apps/web/core/store/issue/issue_calendar_view.store.ts @@ -1,10 +1,13 @@ -import { observable, action, makeObservable, runInAction, computed } from "mobx"; +import { observable, action, makeObservable, runInAction, computed, reaction } from "mobx"; // helpers import { computedFn } from "mobx-utils"; import type { ICalendarPayload, ICalendarWeek } from "@plane/types"; +import { EStartOfTheWeek } from "@plane/types"; import { generateCalendarData, getWeekNumberOfDate } from "@plane/utils"; // types +import type { IIssueRootStore } from "./root.store"; + export interface ICalendarStore { calendarFilters: { activeMonthDate: Date; @@ -15,6 +18,7 @@ export interface ICalendarStore { // action updateCalendarFilters: (filters: Partial<{ activeMonthDate: Date; activeWeekDate: Date }>) => void; updateCalendarPayload: (date: Date) => void; + regenerateCalendar: () => void; // computed allWeeksOfActiveMonth: @@ -38,8 +42,10 @@ export class CalendarStore implements ICalendarStore { activeWeekDate: new Date(), }; calendarPayload: ICalendarPayload | null = null; + // root store + rootStore; - constructor() { + constructor(_rootStore: IIssueRootStore) { makeObservable(this, { loader: observable.ref, error: observable.ref, @@ -51,6 +57,7 @@ export class CalendarStore implements ICalendarStore { // actions updateCalendarFilters: action, updateCalendarPayload: action, + regenerateCalendar: action, //computed allWeeksOfActiveMonth: computed, @@ -58,7 +65,17 @@ export class CalendarStore implements ICalendarStore { allDaysOfActiveWeek: computed, }); + this.rootStore = _rootStore; this.initCalendar(); + + // Watch for changes in startOfWeek preference and regenerate calendar + reaction( + () => this.rootStore.rootStore.user.userProfile.data?.start_of_the_week, + () => { + // Regenerate calendar when startOfWeek preference changes + this.regenerateCalendar(); + } + ); } get allWeeksOfActiveMonth() { @@ -138,14 +155,32 @@ export class CalendarStore implements ICalendarStore { if (!this.calendarPayload) return null; const nextDate = new Date(date); + const startOfWeek = this.rootStore.rootStore.user.userProfile.data?.start_of_the_week ?? EStartOfTheWeek.SUNDAY; runInAction(() => { - this.calendarPayload = generateCalendarData(this.calendarPayload, nextDate); + this.calendarPayload = generateCalendarData(this.calendarPayload, nextDate, startOfWeek); }); }; initCalendar = () => { - const newCalendarPayload = generateCalendarData(null, new Date()); + const startOfWeek = this.rootStore.rootStore.user.userProfile.data?.start_of_the_week ?? EStartOfTheWeek.SUNDAY; + const newCalendarPayload = generateCalendarData(null, new Date(), startOfWeek); + + runInAction(() => { + this.calendarPayload = newCalendarPayload; + }); + }; + + /** + * Force complete regeneration of calendar data + * This should be called when startOfWeek preference changes + */ + regenerateCalendar = () => { + const startOfWeek = this.rootStore.rootStore.user.userProfile.data?.start_of_the_week ?? EStartOfTheWeek.SUNDAY; + const { activeMonthDate } = this.calendarFilters; + + // Force complete regeneration by passing null to clear all cached data + const newCalendarPayload = generateCalendarData(null, activeMonthDate, startOfWeek); runInAction(() => { this.calendarPayload = newCalendarPayload; diff --git a/apps/web/core/store/issue/root.store.ts b/apps/web/core/store/issue/root.store.ts index 02dcfe1845..ad671a5668 100644 --- a/apps/web/core/store/issue/root.store.ts +++ b/apps/web/core/store/issue/root.store.ts @@ -266,7 +266,7 @@ export class IssueRootStore implements IIssueRootStore { this.archivedIssues = new ArchivedIssues(this, this.archivedIssuesFilter); this.issueKanBanView = new IssueKanBanViewStore(this); - this.issueCalendarView = new CalendarStore(); + this.issueCalendarView = new CalendarStore(this); this.projectEpicsFilter = new ProjectEpicsFilter(this); this.projectEpics = new ProjectEpics(this, this.projectEpicsFilter); diff --git a/packages/utils/src/calendar.ts b/packages/utils/src/calendar.ts index a8038ec5ef..6d1f1de574 100644 --- a/packages/utils/src/calendar.ts +++ b/packages/utils/src/calendar.ts @@ -7,9 +7,14 @@ import { getWeekNumberOfDate, renderFormattedPayloadDate } from "./datetime"; * @returns {ICalendarPayload} calendar payload to render the calendar * @param {ICalendarPayload | null} currentStructure current calendar payload * @param {Date} startDate date of the month to render + * @param {EStartOfTheWeek} startOfWeek the day to start the week on * @description Returns calendar payload to render the calendar, if currentStructure is null, it will generate the payload for the month of startDate, else it will construct the payload for the month of startDate and append it to the currentStructure */ -export const generateCalendarData = (currentStructure: ICalendarPayload | null, startDate: Date): ICalendarPayload => { +export const generateCalendarData = ( + currentStructure: ICalendarPayload | null, + startDate: Date, + startOfWeek: EStartOfTheWeek = EStartOfTheWeek.SUNDAY +): ICalendarPayload => { const calendarData: ICalendarPayload = currentStructure ?? {}; const startMonth = startDate.getMonth(); @@ -19,10 +24,15 @@ export const generateCalendarData = (currentStructure: ICalendarPayload | null, const year = currentDate.getFullYear(); const month = currentDate.getMonth(); const totalDaysInMonth = new Date(year, month + 1, 0).getDate(); - const firstDayOfMonth = new Date(year, month, 1).getDay(); // Sunday is 0, Monday is 1, ..., Saturday is 6 + const firstDayOfMonthRaw = new Date(year, month, 1).getDay(); // Sunday is 0, Monday is 1, ..., Saturday is 6 + + // Adjust firstDayOfMonth based on startOfWeek preference + // This calculates how many empty cells we need at the start of the calendar + const firstDayOfMonth = (firstDayOfMonthRaw - startOfWeek + 7) % 7; calendarData[`y-${year}`] ||= {}; - calendarData[`y-${year}`][`m-${month}`] ||= {}; + // Always reset the month data to ensure clean regeneration with correct startOfWeek + calendarData[`y-${year}`][`m-${month}`] = {}; const numWeeks = Math.ceil((totalDaysInMonth + firstDayOfMonth) / 7); @@ -50,7 +60,9 @@ export const generateCalendarData = (currentStructure: ICalendarPayload | null, }; } - calendarData[`y-${year}`][`m-${month}`][`w-${weekNumber}`] = currentWeekObject; + // Use sequential week index instead of calculated week number for the key + // This ensures weeks are grouped correctly regardless of startOfWeek preference + calendarData[`y-${year}`][`m-${month}`][`w-${week}`] = currentWeekObject; } return calendarData; From addf3c410459063f65c652dd0452e16f018881bb Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Thu, 23 Oct 2025 14:53:41 +0530 Subject: [PATCH 2/3] chore: added live server secret key in community docker file --- deployments/cli/community/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/deployments/cli/community/docker-compose.yml b/deployments/cli/community/docker-compose.yml index 8ea3bb5926..8f2eb22308 100644 --- a/deployments/cli/community/docker-compose.yml +++ b/deployments/cli/community/docker-compose.yml @@ -44,6 +44,7 @@ x-mq-env: &mq-env # RabbitMQ Settings x-live-env: &live-env API_BASE_URL: ${API_BASE_URL:-http://api:8000} + LIVE_SERVER_SECRET_KEY: ${LIVE_SERVER_SECRET_KEY:-2FiJk1U2aiVPEQtzLehYGlTSnTnrs7LW} x-app-env: &app-env WEB_URL: ${WEB_URL:-http://localhost} From b34c9ca04f060899294109e2cf0ee17608152204 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 23 Oct 2025 15:42:43 +0530 Subject: [PATCH 3/3] fix: live server secret key app version env variables (#7997) * fix: live server secret key app version env variables * fix: revert to stable version * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deployments/cli/community/docker-compose.yml | 1 + deployments/cli/community/variables.env | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/deployments/cli/community/docker-compose.yml b/deployments/cli/community/docker-compose.yml index 8f2eb22308..3833b96bad 100644 --- a/deployments/cli/community/docker-compose.yml +++ b/deployments/cli/community/docker-compose.yml @@ -57,6 +57,7 @@ x-app-env: &app-env AMQP_URL: ${AMQP_URL:-amqp://plane:plane@plane-mq:5672/plane} API_KEY_RATE_LIMIT: ${API_KEY_RATE_LIMIT:-60/minute} MINIO_ENDPOINT_SSL: ${MINIO_ENDPOINT_SSL:-0} + LIVE_SERVER_SECRET_KEY: ${LIVE_SERVER_SECRET_KEY:-2FiJk1U2aiVPEQtzLehYGlTSnTnrs7LW} services: web: diff --git a/deployments/cli/community/variables.env b/deployments/cli/community/variables.env index 9a8d52edcf..5a6c03f531 100644 --- a/deployments/cli/community/variables.env +++ b/deployments/cli/community/variables.env @@ -76,3 +76,7 @@ MINIO_ENDPOINT_SSL=0 # API key rate limit API_KEY_RATE_LIMIT=60/minute + +# Live server environment variables +# WARNING: You must set a secure value for LIVE_SERVER_SECRET_KEY in production environments. +LIVE_SERVER_SECRET_KEY=