diff --git a/src/components/dialogs/UserDialog/UserDialogActivityTab.vue b/src/components/dialogs/UserDialog/UserDialogActivityTab.vue index 7cea6011..3f45e711 100644 --- a/src/components/dialogs/UserDialog/UserDialogActivityTab.vue +++ b/src/components/dialogs/UserDialog/UserDialogActivityTab.vue @@ -150,7 +150,7 @@ const { t, locale } = useI18n(); const { userDialog } = storeToRefs(useUserStore()); - const { isDarkMode } = storeToRefs(useAppearanceSettingsStore()); + const { isDarkMode, weekStartsOn } = storeToRefs(useAppearanceSettingsStore()); const chartRef = ref(null); const isLoading = ref(false); @@ -190,16 +190,10 @@ t('dialog.user.activity.days.sat') ]); - // Reorder: Mon-Sun for display (row 0=Mon at top, row 6=Sun at bottom) - const displayDayLabels = computed(() => [ - dayLabels.value[1], // Mon - dayLabels.value[2], // Tue - dayLabels.value[3], // Wed - dayLabels.value[4], // Thu - dayLabels.value[5], // Fri - dayLabels.value[6], // Sat - dayLabels.value[0] // Sun - ]); + const displayDayLabels = computed(() => { + const start = weekStartsOn.value; + return Array.from({ length: 7 }, (_, i) => dayLabels.value[(start + i) % 7]); + }); const hourLabels = Array.from({ length: 24 }, (_, i) => `${String(i).padStart(2, '0')}:00`); @@ -230,6 +224,7 @@ watch(() => isDarkMode.value, rebuildChart); watch(locale, rebuildChart); + watch(weekStartsOn, rebuildChart); watch(selectedPeriod, () => { if (cachedTargetSessions.length > 0 && echartsInstance) { initChart(); @@ -356,10 +351,11 @@ peakTimeText.value = peakTimeResult; const data = []; + const wso = weekStartsOn.value; for (let day = 0; day < 7; day++) { for (let hour = 0; hour < 24; hour++) { const count = grid[day][hour]; - const displayDay = day === 0 ? 6 : day - 1; + const displayDay = (day - wso + 7) % 7; data.push([hour, displayDay, count]); } } @@ -685,10 +681,11 @@ if (!overlapEchartsInstance) return; const data = []; + const wso = weekStartsOn.value; for (let day = 0; day < 7; day++) { for (let hour = 0; hour < 24; hour++) { const count = result.grid[day][hour]; - const displayDay = day === 0 ? 6 : day - 1; + const displayDay = (day - wso + 7) % 7; data.push([hour, displayDay, count]); } } diff --git a/src/localization/en.json b/src/localization/en.json index f542815f..ce18f71f 100644 --- a/src/localization/en.json +++ b/src/localization/en.json @@ -23,6 +23,15 @@ "h": "h", "m": "m", "s": "s" + }, + "days": { + "sunday": "Sunday", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday" } }, "nav_tooltip": { @@ -874,7 +883,9 @@ "time_format": "Time Format", "time_format_24": "24 Hour", "time_format_12": "12 Hour", - "force_iso_date_format": "Force ISO Date Format" + "force_iso_date_format": "Force ISO Date Format", + "week_starts_on": "Week Starts On", + "week_starts_on_description": "Choose the first day of the week. Affects calendars, charts, and activity views." }, "theme_color": { "header": "Theme Color", diff --git a/src/stores/settings/appearance.js b/src/stores/settings/appearance.js index 1621f559..43953a3e 100644 --- a/src/stores/settings/appearance.js +++ b/src/stores/settings/appearance.js @@ -70,6 +70,7 @@ export const useAppearanceSettingsStore = defineStore( const tablePageSizes = ref([...DEFAULT_TABLE_PAGE_SIZES]); const dtHour12 = ref(false); const dtIsoFormat = ref(false); + const weekStartsOn = ref(1); const sidebarSortMethod1 = ref('Sort Private to Bottom'); const sidebarSortMethod2 = ref('Sort by Time in Instance'); const sidebarSortMethod3 = ref('Sort by Last Active'); @@ -159,6 +160,7 @@ export const useAppearanceSettingsStore = defineStore( tablePageSizesConfig, dtHour12Config, dtIsoFormatConfig, + weekStartsOnConfig, sidebarSortMethodsConfig, navWidthConfig, isSidebarGroupByInstanceConfig, @@ -209,6 +211,7 @@ export const useAppearanceSettingsStore = defineStore( ), configRepository.getBool('VRCX_dtHour12', false), configRepository.getBool('VRCX_dtIsoFormat', false), + configRepository.getInt('VRCX_weekStartsOn', 1), configRepository.getString( 'VRCX_sidebarSortMethods', JSON.stringify([ @@ -250,7 +253,10 @@ export const useAppearanceSettingsStore = defineStore( configRepository.getBool('VRCX_navIsCollapsed', false), configRepository.getBool('VRCX_dataTableStriped', false), configRepository.getBool('VRCX_showPointerOnHover', false), - configRepository.getBool('VRCX_accessibleStatusIndicators', false), + configRepository.getBool( + 'VRCX_accessibleStatusIndicators', + false + ), configRepository.getBool('VRCX_useOfficialStatusColors', true), configRepository.getBool('VRCX_showNewDashboardButton', true), configRepository.getString( @@ -317,6 +323,9 @@ export const useAppearanceSettingsStore = defineStore( dtHour12.value = dtHour12Config; dtIsoFormat.value = dtIsoFormatConfig; + weekStartsOn.value = [0, 1, 6].includes(weekStartsOnConfig) + ? weekStartsOnConfig + : 1; currentCulture.value = await AppApi.CurrentCulture(); @@ -724,6 +733,14 @@ export const useAppearanceSettingsStore = defineStore( dtIsoFormat.value = !dtIsoFormat.value; configRepository.setBool('VRCX_dtIsoFormat', dtIsoFormat.value); } + /** + * @param {number} value - 0 (Sunday), 1 (Monday), or 6 (Saturday) + */ + function setWeekStartsOn(value) { + const v = [0, 1, 6].includes(value) ? value : 1; + weekStartsOn.value = v; + configRepository.setInt('VRCX_weekStartsOn', v); + } /** * @param {string} method */ @@ -962,7 +979,8 @@ export const useAppearanceSettingsStore = defineStore( * */ function toggleAccessibleStatusIndicators() { - accessibleStatusIndicators.value = !accessibleStatusIndicators.value; + accessibleStatusIndicators.value = + !accessibleStatusIndicators.value; configRepository.setBool( 'VRCX_accessibleStatusIndicators', accessibleStatusIndicators.value @@ -1216,6 +1234,7 @@ export const useAppearanceSettingsStore = defineStore( tablePageSizes, dtHour12, dtIsoFormat, + weekStartsOn, sidebarSortMethod1, sidebarSortMethod2, sidebarSortMethod3, @@ -1259,6 +1278,7 @@ export const useAppearanceSettingsStore = defineStore( setTablePageSizes, setDtHour12, setDtIsoFormat, + setWeekStartsOn, setSidebarSortMethod1, setSidebarSortMethod2, setSidebarSortMethod3, diff --git a/src/views/Charts/components/InstanceActivity.vue b/src/views/Charts/components/InstanceActivity.vue index c560040e..1b9bddd6 100644 --- a/src/views/Charts/components/InstanceActivity.vue +++ b/src/views/Charts/components/InstanceActivity.vue @@ -131,6 +131,7 @@ :default-placeholder="defaultCalendarPlaceholder" :is-date-disabled="isCalendarDateDisabled" :prevent-deselect="true" + :week-starts-on="weekStartsOn" initial-focus @update:modelValue="handleCalendarModelUpdate" /> @@ -216,7 +217,7 @@ const { friends, allFavoriteFriendIds } = storeToRefs(friendStore); const { currentUser } = storeToRefs(useUserStore()); const { t } = useI18n(); - const { isDarkMode, dtHour12 } = storeToRefs(appearanceSettingsStore); + const { isDarkMode, dtHour12, weekStartsOn } = storeToRefs(appearanceSettingsStore); const instanceActivityRef = ref(null); @@ -596,15 +597,15 @@ const location = parseLocation(instanceData.location); return ` -
-
-
-
${name} #${location.instanceName} ${location.accessTypeName}
-
${formattedJoinDateTime} - ${formattedLeftDateTime}
-
${timeString}
+
+
+
+
${name} #${location.instanceName} ${location.accessTypeName}
+
${formattedJoinDateTime} - ${formattedLeftDateTime}
+
${timeString}
+
-
- `; + `; }; const format = dtHour12.value ? 'hh:mm A' : 'HH:mm'; diff --git a/src/views/Feed/Feed.vue b/src/views/Feed/Feed.vue index 8f234f6e..a3c46a45 100644 --- a/src/views/Feed/Feed.vue +++ b/src/views/Feed/Feed.vue @@ -28,7 +28,8 @@ v-model="dateRange" :locale="locale" :max-value="todayDate" - :number-of-months="2" /> + :number-of-months="2" + :week-starts-on="weekStartsOn" />