Allow customisable Weekend days
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
export let optimizedDaysOff: Date[];
|
||||
export let consecutiveDaysOff: Array<{ startDate: Date; endDate: Date; totalDays: number }>;
|
||||
export let selectedCountryCode: string;
|
||||
export let weekendDays: number[] = [6, 0];
|
||||
|
||||
// Function to determine the first day of the week based on locale
|
||||
function getFirstDayOfWeek(locale: string): number {
|
||||
@@ -80,11 +81,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
function isWeekend(day: number): boolean {
|
||||
const dayOfWeek = (adjustedFirstDay + day - 1) % 7;
|
||||
const saturdayIndex = (6 - firstDayOfWeek + 7) % 7;
|
||||
const sundayIndex = (7 - firstDayOfWeek + 7) % 7;
|
||||
return dayOfWeek === saturdayIndex || dayOfWeek === sundayIndex;
|
||||
function isWeekend(date: Date): boolean {
|
||||
return weekendDays.includes(date.getDay());
|
||||
}
|
||||
|
||||
const dayInitials = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
|
||||
@@ -103,7 +101,7 @@
|
||||
<div class="day"></div>
|
||||
{/each}
|
||||
{#each Array.from({ length: daysInMonth }, (_, i) => i + 1) as day}
|
||||
<div class="day {isWeekend(day) ? 'weekend' : ''} {getHoliday(day) ? 'holiday' : ''} {isOptimizedDayOff(day) ? 'optimized' : ''} {isConsecutiveDayOff(day) ? 'consecutive-day' : ''}">
|
||||
<div class="day {isWeekend(new Date(year, month, day)) ? 'weekend' : ''} {getHoliday(day) ? 'holiday' : ''} {isOptimizedDayOff(day) ? 'optimized' : ''} {isConsecutiveDayOff(day) ? 'consecutive-day' : ''}">
|
||||
{day}
|
||||
{#if getHoliday(day)}
|
||||
<Tooltip text={getHoliday(day)?.name} />
|
||||
|
||||
@@ -5,7 +5,7 @@ const MS_IN_A_DAY = 86400000;
|
||||
const MAX_GAP_LENGTH = 5;
|
||||
|
||||
// Helper function to check if a date is a weekend
|
||||
const isWeekend = (date: Date): boolean => date.getDay() === 0 || date.getDay() === 6;
|
||||
const isWeekend = (date: Date, weekendDays: number[]): boolean => weekendDays.includes(date.getDay());
|
||||
|
||||
// Helper function to check if two dates are the same day
|
||||
const isSameDay = (date1: Date, date2: Date): boolean =>
|
||||
@@ -17,7 +17,7 @@ const isSameDay = (date1: Date, date2: Date): boolean =>
|
||||
const dateKey = (date: Date): string => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
|
||||
|
||||
// Helper function to check if a date is a holiday
|
||||
const isHoliday = (date: Date, holidays: { date: Date }[]): boolean => holidays.some(h => isSameDay(h.date, date));
|
||||
const isHoliday = (date: Date, holidays: { date: Date }[]): boolean => holidays.some(h => dateKey(h.date) === dateKey(date));
|
||||
|
||||
// Helper function to check if a date is a day off
|
||||
const isDayOff = (date: Date, allDaysOffSet: Set<string>): boolean => allDaysOffSet.has(dateKey(date));
|
||||
@@ -50,22 +50,49 @@ export function getHolidaysForYear(countryCode: string, year: number, stateCode?
|
||||
}
|
||||
|
||||
// Optimize days off to create the longest possible chains
|
||||
export function optimizeDaysOff(holidays: { date: Date }[], year: number, daysOff: number): Date[] {
|
||||
export function optimizeDaysOff(
|
||||
holidays: { date: Date }[],
|
||||
year: number,
|
||||
daysOff: number,
|
||||
weekendDays: number[] = [0, 6]
|
||||
): Date[] {
|
||||
const currentYearHolidays = holidays.filter(h => h.date.getFullYear() === year);
|
||||
const weekends = getWeekends(year);
|
||||
const weekends = getWeekends(year, weekendDays);
|
||||
const allDaysOffSet = new Set([
|
||||
...currentYearHolidays.map(h => dateKey(h.date)),
|
||||
...weekends.map(d => dateKey(d))
|
||||
]);
|
||||
|
||||
let rankedGaps = rankGapsByEfficiency(findGaps(allDaysOffSet, year), allDaysOffSet);
|
||||
let rankedGaps = rankGapsByEfficiency(
|
||||
findGaps(allDaysOffSet, year, weekendDays),
|
||||
allDaysOffSet,
|
||||
weekendDays
|
||||
);
|
||||
|
||||
return selectDaysOff(rankedGaps, daysOff, allDaysOffSet, year);
|
||||
return selectDaysOff(rankedGaps, daysOff, allDaysOffSet, year, weekendDays);
|
||||
}
|
||||
|
||||
// Calculate consecutive days off
|
||||
export function calculateConsecutiveDaysOff(holidays: { date: Date }[], optimizedDaysOff: Date[], year: number): { startDate: Date; endDate: Date; usedDaysOff: number; totalDays: number }[] {
|
||||
const allDaysOffSet = new Set([...holidays.map(h => dateKey(h.date)), ...optimizedDaysOff.map(d => dateKey(d))]);
|
||||
export function calculateConsecutiveDaysOff(
|
||||
holidays: Array<{ date: Date }>,
|
||||
optimizedDaysOff: Date[],
|
||||
year: number,
|
||||
weekendDays: number[] = [0, 6]
|
||||
) {
|
||||
const allDaysOff = new Set([
|
||||
...holidays.map(h => h.date.toISOString()),
|
||||
...optimizedDaysOff.map(d => d.toISOString())
|
||||
]);
|
||||
|
||||
// Add all weekend days for the year
|
||||
const startDate = new Date(year, 0, 1);
|
||||
const endDate = new Date(year, 11, 31);
|
||||
for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
|
||||
if (weekendDays.includes(d.getDay())) {
|
||||
allDaysOff.add(new Date(d).toISOString());
|
||||
}
|
||||
}
|
||||
|
||||
const consecutiveDaysOff: { startDate: Date; endDate: Date; usedDaysOff: number; totalDays: number }[] = [];
|
||||
let currentGroup: Date[] = [];
|
||||
|
||||
@@ -74,39 +101,102 @@ export function calculateConsecutiveDaysOff(holidays: { date: Date }[], optimize
|
||||
const date = new Date(year, month, day);
|
||||
if (date.getMonth() !== month) break;
|
||||
|
||||
if (isWeekend(date) || isHoliday(date, holidays) || isDayOff(date, allDaysOffSet)) {
|
||||
if (isWeekend(date, weekendDays) || isHoliday(date, holidays) || allDaysOff.has(date.toISOString())) {
|
||||
currentGroup.push(date);
|
||||
} else {
|
||||
if (currentGroup.length > 2) {
|
||||
addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff);
|
||||
if (isValidPeriod(currentGroup, weekendDays, holidays, optimizedDaysOff)) {
|
||||
const startDate = currentGroup[0];
|
||||
const endDate = currentGroup[currentGroup.length - 1];
|
||||
const totalDays = daysBetween(startDate, endDate) + 1;
|
||||
const usedDaysOff = currentGroup.filter(d =>
|
||||
optimizedDaysOff.some(od =>
|
||||
od.getFullYear() === d.getFullYear() &&
|
||||
od.getMonth() === d.getMonth() &&
|
||||
od.getDate() === d.getDate()
|
||||
)
|
||||
).length;
|
||||
|
||||
consecutiveDaysOff.push({
|
||||
startDate,
|
||||
endDate,
|
||||
usedDaysOff,
|
||||
totalDays
|
||||
});
|
||||
}
|
||||
currentGroup = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentGroup.length > 2) {
|
||||
addConsecutiveDaysOff(consecutiveDaysOff, currentGroup, optimizedDaysOff);
|
||||
if (isValidPeriod(currentGroup, weekendDays, holidays, optimizedDaysOff)) {
|
||||
const startDate = currentGroup[0];
|
||||
const endDate = currentGroup[currentGroup.length - 1];
|
||||
const totalDays = daysBetween(startDate, endDate) + 1;
|
||||
const usedDaysOff = currentGroup.filter(d =>
|
||||
optimizedDaysOff.some(od =>
|
||||
od.getFullYear() === d.getFullYear() &&
|
||||
od.getMonth() === d.getMonth() &&
|
||||
od.getDate() === d.getDate()
|
||||
)
|
||||
).length;
|
||||
|
||||
consecutiveDaysOff.push({
|
||||
startDate,
|
||||
endDate,
|
||||
usedDaysOff,
|
||||
totalDays
|
||||
});
|
||||
}
|
||||
|
||||
return consecutiveDaysOff;
|
||||
}
|
||||
|
||||
// Update the isValidPeriod function
|
||||
function isValidPeriod(
|
||||
group: Date[],
|
||||
weekendDays: number[],
|
||||
holidays: Array<{ date: Date }>,
|
||||
optimizedDaysOff: Date[]
|
||||
): boolean {
|
||||
if (group.length < 2) return false;
|
||||
|
||||
// Count weekend days
|
||||
const weekendDates = group.filter(date => weekendDays.includes(date.getDay()));
|
||||
if (weekendDates.length === 0) return false; // Must have at least one weekend day
|
||||
|
||||
// Count non-weekend days that are either holidays or PTO days
|
||||
const nonWeekendHolidayOrPTO = group.some(date => {
|
||||
// Skip if it's a weekend day
|
||||
if (weekendDays.includes(date.getDay())) return false;
|
||||
|
||||
// Check if it's either a holiday or PTO day
|
||||
return isHoliday(date, holidays) ||
|
||||
optimizedDaysOff.some(od =>
|
||||
od.getFullYear() === date.getFullYear() &&
|
||||
od.getMonth() === date.getMonth() &&
|
||||
od.getDate() === date.getDate()
|
||||
);
|
||||
});
|
||||
|
||||
// Must have at least one weekend day AND one non-weekend holiday/PTO day
|
||||
return nonWeekendHolidayOrPTO;
|
||||
}
|
||||
|
||||
// Get all weekends for a specific year
|
||||
function getWeekends(year: number): Date[] {
|
||||
function getWeekends(year: number, weekendDays: number[]): Date[] {
|
||||
const weekends: Date[] = [];
|
||||
for (let month = 0; month < 12; month++) {
|
||||
for (let day = 1; day <= 31; day++) {
|
||||
const date = new Date(year, month, day);
|
||||
if (date.getMonth() !== month) break;
|
||||
if (isWeekend(date)) weekends.push(date);
|
||||
if (isWeekend(date, weekendDays)) weekends.push(date);
|
||||
}
|
||||
}
|
||||
return weekends;
|
||||
}
|
||||
|
||||
// Find gaps between days off
|
||||
function findGaps(allDaysOffSet: Set<string>, year: number): { start: Date; end: Date; gapLength: number }[] {
|
||||
function findGaps(allDaysOffSet: Set<string>, year: number, weekendDays: number[]): { start: Date; end: Date; gapLength: number }[] {
|
||||
const gaps: { start: Date; end: Date; gapLength: number }[] = [];
|
||||
let currentGapStart: Date | null = null;
|
||||
|
||||
@@ -117,7 +207,7 @@ function findGaps(allDaysOffSet: Set<string>, year: number): { start: Date; end:
|
||||
|
||||
const isDayOff = allDaysOffSet.has(dateKey(date));
|
||||
|
||||
if (!isDayOff && !isWeekend(date)) {
|
||||
if (!isDayOff && !isWeekend(date, weekendDays)) {
|
||||
if (!currentGapStart) currentGapStart = date;
|
||||
} else if (currentGapStart) {
|
||||
const gapLength = daysBetween(currentGapStart, date);
|
||||
@@ -141,19 +231,34 @@ function findGaps(allDaysOffSet: Set<string>, year: number): { start: Date; end:
|
||||
}
|
||||
|
||||
// Rank gaps by efficiency
|
||||
function rankGapsByEfficiency(gaps: { start: Date; end: Date; gapLength: number }[], allDaysOffSet: Set<string>): any[] {
|
||||
function rankGapsByEfficiency(
|
||||
gaps: { start: Date; end: Date; gapLength: number }[],
|
||||
allDaysOffSet: Set<string>,
|
||||
weekendDays: number[] = [0, 6]
|
||||
): any[] {
|
||||
return gaps.map(gap => {
|
||||
const backward = calculateChain(gap.start, gap.gapLength, allDaysOffSet, 'backward');
|
||||
const forward = calculateChain(gap.end, gap.gapLength, allDaysOffSet, 'forward');
|
||||
const backward = calculateChain(gap.start, gap.gapLength, allDaysOffSet, 'backward', weekendDays);
|
||||
const forward = calculateChain(gap.end, gap.gapLength, allDaysOffSet, 'forward', weekendDays);
|
||||
|
||||
return forward.chainLength > backward.chainLength || (forward.chainLength === backward.chainLength && forward.usedDaysOff <= backward.usedDaysOff)
|
||||
return forward.chainLength > backward.chainLength ||
|
||||
(forward.chainLength === backward.chainLength && forward.usedDaysOff <= backward.usedDaysOff)
|
||||
? { ...gap, ...forward, fillFrom: 'end' }
|
||||
: { ...gap, ...backward, fillFrom: 'start' };
|
||||
}).sort((a, b) => a.gapLength - b.gapLength || b.chainLength - a.chainLength || a.usedDaysOff - b.usedDaysOff);
|
||||
}).sort((a, b) =>
|
||||
a.gapLength - b.gapLength ||
|
||||
b.chainLength - a.chainLength ||
|
||||
a.usedDaysOff - b.usedDaysOff
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate potential chain length and days off used
|
||||
function calculateChain(startDate: Date, gapLength: number, allDaysOffSet: Set<string>, direction: 'backward' | 'forward'): { chainLength: number; usedDaysOff: number } {
|
||||
function calculateChain(
|
||||
startDate: Date,
|
||||
gapLength: number,
|
||||
allDaysOffSet: Set<string>,
|
||||
direction: 'backward' | 'forward',
|
||||
weekendDays: number[] = [0, 6]
|
||||
): { chainLength: number; usedDaysOff: number } {
|
||||
let chainLength = gapLength;
|
||||
let usedDaysOff = 0;
|
||||
let currentDate = new Date(startDate);
|
||||
@@ -161,7 +266,8 @@ function calculateChain(startDate: Date, gapLength: number, allDaysOffSet: Set<s
|
||||
const increment = direction === 'backward' ? -1 : 1;
|
||||
const boundaryCheck = direction === 'backward' ? -MS_IN_A_DAY : MS_IN_A_DAY;
|
||||
|
||||
while (allDaysOffSet.has(dateKey(new Date(currentDate.getTime() + boundaryCheck))) || isWeekend(new Date(currentDate.getTime() + boundaryCheck))) {
|
||||
while (allDaysOffSet.has(dateKey(new Date(currentDate.getTime() + boundaryCheck))) ||
|
||||
isWeekend(new Date(currentDate.getTime() + boundaryCheck), weekendDays)) {
|
||||
chainLength++;
|
||||
currentDate.setDate(currentDate.getDate() + increment);
|
||||
}
|
||||
@@ -169,7 +275,7 @@ function calculateChain(startDate: Date, gapLength: number, allDaysOffSet: Set<s
|
||||
for (let i = 0; i < gapLength; i++) {
|
||||
const potentialDayOff = new Date(startDate);
|
||||
potentialDayOff.setDate(potentialDayOff.getDate() + (i * increment));
|
||||
if (!allDaysOffSet.has(dateKey(potentialDayOff)) && !isWeekend(potentialDayOff)) {
|
||||
if (!allDaysOffSet.has(dateKey(potentialDayOff)) && !isWeekend(potentialDayOff, weekendDays)) {
|
||||
usedDaysOff++;
|
||||
}
|
||||
}
|
||||
@@ -178,7 +284,7 @@ function calculateChain(startDate: Date, gapLength: number, allDaysOffSet: Set<s
|
||||
}
|
||||
|
||||
// Select days off based on ranked gaps
|
||||
function selectDaysOff(rankedGaps: any[], daysOff: number, allDaysOffSet: Set<string>, year: number): Date[] {
|
||||
function selectDaysOff(rankedGaps: any[], daysOff: number, allDaysOffSet: Set<string>, year: number, weekendDays: number[]): Date[] {
|
||||
const selectedDays: Date[] = [];
|
||||
|
||||
while (daysOff > 0 && rankedGaps.length > 0) {
|
||||
@@ -191,33 +297,40 @@ function selectDaysOff(rankedGaps: any[], daysOff: number, allDaysOffSet: Set<st
|
||||
const potentialDayOff = new Date(startDate);
|
||||
potentialDayOff.setDate(potentialDayOff.getDate() + (i * increment));
|
||||
|
||||
if (!allDaysOffSet.has(dateKey(potentialDayOff)) && !isWeekend(potentialDayOff)) {
|
||||
if (!allDaysOffSet.has(dateKey(potentialDayOff)) && !isWeekend(potentialDayOff, weekendDays)) {
|
||||
selectedDays.push(potentialDayOff);
|
||||
allDaysOffSet.add(dateKey(potentialDayOff));
|
||||
daysOff--;
|
||||
}
|
||||
}
|
||||
|
||||
const newGaps = findGaps(allDaysOffSet, year);
|
||||
rankedGaps = rankGapsByEfficiency(newGaps, allDaysOffSet);
|
||||
const newGaps = findGaps(allDaysOffSet, year, weekendDays);
|
||||
rankedGaps = rankGapsByEfficiency(newGaps, allDaysOffSet, weekendDays);
|
||||
}
|
||||
|
||||
return selectedDays;
|
||||
}
|
||||
|
||||
// Add consecutive days off to the list
|
||||
function addConsecutiveDaysOff(consecutiveDaysOff: { startDate: Date; endDate: Date; usedDaysOff: number; totalDays: number }[], currentGroup: Date[], optimizedDaysOff: Date[]) {
|
||||
function addConsecutiveDaysOff(
|
||||
consecutiveDaysOff: { startDate: Date; endDate: Date; usedDaysOff: number; totalDays: number }[],
|
||||
currentGroup: Date[],
|
||||
optimizedDaysOff: Date[]
|
||||
) {
|
||||
const startDate = currentGroup[0];
|
||||
const endDate = currentGroup[currentGroup.length - 1];
|
||||
const totalDays = daysBetween(startDate, endDate) + 1;
|
||||
const usedDaysOff = currentGroup.filter(d => isDayOff(d, new Set(optimizedDaysOff.map(d => dateKey(d))))).length;
|
||||
|
||||
// Create a Set of optimized days off for faster lookup
|
||||
const optimizedDaysOffSet = new Set(optimizedDaysOff.map(d => dateKey(d)));
|
||||
|
||||
// Count only the days that were actually used from our PTO days
|
||||
const usedDaysOff = currentGroup.filter(d => optimizedDaysOffSet.has(dateKey(d))).length;
|
||||
|
||||
if (totalDays > 2) {
|
||||
consecutiveDaysOff.push({
|
||||
startDate,
|
||||
endDate,
|
||||
usedDaysOff,
|
||||
totalDays
|
||||
});
|
||||
}
|
||||
consecutiveDaysOff.push({
|
||||
startDate,
|
||||
endDate,
|
||||
usedDaysOff,
|
||||
totalDays
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { injectSpeedInsights } from '@vercel/speed-insights';
|
||||
import { inject } from '@vercel/analytics';
|
||||
import countries from 'i18n-iso-countries';
|
||||
import enLocale from 'i18n-iso-countries/langs/en.json';
|
||||
import CalendarMonth from '../lib/CalendarMonth.svelte';
|
||||
@@ -34,6 +32,9 @@
|
||||
|
||||
let showHolidaysList: boolean = false;
|
||||
|
||||
let showWeekendSettings: boolean = false;
|
||||
let weekendDays: number[] = [];
|
||||
|
||||
$: selectedCountryCode = Object.keys(countriesList).find(code => countriesList[code] === selectedCountry) || '';
|
||||
|
||||
$: if (selectedCountryCode || selectedStateCode || daysOff || year) {
|
||||
@@ -63,8 +64,6 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
inject();
|
||||
injectSpeedInsights();
|
||||
|
||||
fetchCountryCode().then(() => {
|
||||
defaultYear = new Date().getFullYear();
|
||||
@@ -89,6 +88,14 @@
|
||||
updateStatesList(selectedCountryCode);
|
||||
}
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
// Load weekend days from localStorage or set defaults
|
||||
const storedWeekendDays = localStorage.getItem('weekendDays');
|
||||
weekendDays = storedWeekendDays
|
||||
? JSON.parse(storedWeekendDays)
|
||||
: [6, 0]; // Default to Saturday (6) and Sunday (0)
|
||||
|
||||
localStorage.setItem('weekendDays', JSON.stringify(weekendDays));
|
||||
});
|
||||
|
||||
async function fetchCountryCode() {
|
||||
@@ -127,10 +134,9 @@
|
||||
date: new Date(holiday.date),
|
||||
hidden: isHolidayHidden(holiday)
|
||||
}));
|
||||
const visibleHolidays = holidays
|
||||
.filter(h => !h.hidden);
|
||||
optimizedDaysOff = optimizeDaysOff(visibleHolidays, year, daysOff);
|
||||
consecutiveDaysOff = calculateConsecutiveDaysOff(visibleHolidays, optimizedDaysOff, year);
|
||||
const visibleHolidays = holidays.filter(h => !h.hidden);
|
||||
optimizedDaysOff = optimizeDaysOff(visibleHolidays, year, daysOff, weekendDays);
|
||||
consecutiveDaysOff = calculateConsecutiveDaysOff(visibleHolidays, optimizedDaysOff, year, weekendDays);
|
||||
} else {
|
||||
holidays = [];
|
||||
optimizedDaysOff = [];
|
||||
@@ -245,6 +251,46 @@
|
||||
}
|
||||
|
||||
$: visibleHolidaysCount = holidays.filter(h => !h.hidden).length;
|
||||
|
||||
function toggleWeekendDay(dayNumber: number) {
|
||||
if (weekendDays.includes(dayNumber)) {
|
||||
weekendDays = weekendDays.filter(d => d !== dayNumber);
|
||||
} else {
|
||||
weekendDays = [...weekendDays, dayNumber];
|
||||
}
|
||||
weekendDays.sort();
|
||||
localStorage.setItem('weekendDays', JSON.stringify(weekendDays));
|
||||
updateHolidays();
|
||||
}
|
||||
|
||||
function getFirstDayOfWeek(locale: string): number {
|
||||
const normalizedLocale = locale.toLowerCase() === 'us' ? 'en-US' : `en-${locale.toUpperCase()}`;
|
||||
try {
|
||||
// @ts-ignore
|
||||
const weekFirstDay = new Intl.Locale(normalizedLocale)?.weekInfo?.firstDay;
|
||||
if (weekFirstDay !== undefined) {
|
||||
return weekFirstDay;
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback if weekInfo is not supported
|
||||
}
|
||||
return normalizedLocale === 'en-US' ? 0 : 1;
|
||||
}
|
||||
|
||||
function getOrderedDays() {
|
||||
const days = [
|
||||
{ name: 'Sunday', index: 0 },
|
||||
{ name: 'Monday', index: 1 },
|
||||
{ name: 'Tuesday', index: 2 },
|
||||
{ name: 'Wednesday', index: 3 },
|
||||
{ name: 'Thursday', index: 4 },
|
||||
{ name: 'Friday', index: 5 },
|
||||
{ name: 'Saturday', index: 6 }
|
||||
];
|
||||
|
||||
const firstDay = getFirstDayOfWeek(selectedCountryCode || 'US');
|
||||
return [...days.slice(firstDay), ...days.slice(0, firstDay)];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -336,23 +382,27 @@
|
||||
.calendar-key {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.key-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 10px;
|
||||
font-size: 0.9em;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.key-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-right: 5px;
|
||||
min-width: 15px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@@ -537,6 +587,9 @@
|
||||
|
||||
.holidays-list {
|
||||
margin: 10px;
|
||||
padding: 15px;
|
||||
background-color: #222;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.holidays-list ul {
|
||||
@@ -547,7 +600,18 @@
|
||||
.holidays-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.holidays-list li:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.holidays-list span {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.holidays-list button {
|
||||
@@ -558,11 +622,26 @@
|
||||
padding: 5px 25px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.holidays-list button:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.settings-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.settings-section h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<main>
|
||||
@@ -632,37 +711,74 @@
|
||||
<div class="content-box" id="calendar">
|
||||
<div class="calendar-key">
|
||||
<div class="key-item">
|
||||
<span class="color-box weekend"></span> Weekend
|
||||
</div>
|
||||
<div class="key-item">
|
||||
<span class="color-box optimized"></span> Day Off
|
||||
</div>
|
||||
<div class="key-item">
|
||||
<span class="color-box holiday"></span> Public Holiday
|
||||
</div>
|
||||
{#if holidays.length > 0}
|
||||
<a href="#" on:click|preventDefault={() => showHolidaysList = !showHolidaysList} class="edit-link">
|
||||
(edit list)
|
||||
<div class="key-label">
|
||||
<span class="color-box weekend"></span>
|
||||
<span>Weekend</span>
|
||||
</div>
|
||||
<a href="#" on:click|preventDefault={() => showWeekendSettings = !showWeekendSettings} class="edit-link">
|
||||
(edit)
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="key-item">
|
||||
<div class="key-label">
|
||||
<span class="color-box optimized"></span>
|
||||
<span>Day Off</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-item">
|
||||
<div class="key-label">
|
||||
<span class="color-box holiday"></span>
|
||||
<span>Public Holiday</span>
|
||||
</div>
|
||||
{#if holidays.length > 0}
|
||||
<a href="#" on:click|preventDefault={() => showHolidaysList = !showHolidaysList} class="edit-link">
|
||||
(edit)
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showHolidaysList}
|
||||
{#if showHolidaysList || showWeekendSettings}
|
||||
<div class="holidays-list">
|
||||
<h3>Public Holidays</h3>
|
||||
<ul>
|
||||
{#each holidays as holiday}
|
||||
<li>
|
||||
<span class="color-box holiday"></span>
|
||||
<span class={holiday.hidden ? 'strikethrough' : ''}>
|
||||
{formatDate(holiday.date)}: {holiday.name}
|
||||
</span>
|
||||
<button on:click={() => toggleHolidayVisibility(holiday)}>
|
||||
{holiday.hidden ? 'Show' : 'Hide'}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{#if showHolidaysList}
|
||||
<div class="settings-section">
|
||||
<h3>Public Holidays</h3>
|
||||
<ul>
|
||||
{#each holidays as holiday}
|
||||
<li>
|
||||
<div class="setting-item-label">
|
||||
<span class="color-box holiday"></span>
|
||||
<span class={holiday.hidden ? 'strikethrough' : ''}>
|
||||
{formatDate(holiday.date)}: {holiday.name}
|
||||
</span>
|
||||
</div>
|
||||
<button on:click={() => toggleHolidayVisibility(holiday)}>
|
||||
{holiday.hidden ? 'Show' : 'Hide'}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showWeekendSettings}
|
||||
<div class="settings-section">
|
||||
<h3>Weekend Days</h3>
|
||||
<ul>
|
||||
{#each getOrderedDays() as {name, index}}
|
||||
<li>
|
||||
<div class="setting-item-label">
|
||||
<span class="color-box weekend"></span>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
<button on:click={() => toggleWeekendDay(index)}>
|
||||
{weekendDays.includes(index) ? 'Remove' : 'Add'}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -676,6 +792,7 @@
|
||||
optimizedDaysOff={optimizedDaysOff}
|
||||
consecutiveDaysOff={consecutiveDaysOff}
|
||||
selectedCountryCode={selectedCountryCode}
|
||||
weekendDays={weekendDays}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user