Enhance calendar functionality in personal section: Implement detailed calendar views (month, week, workweek, day) in CalendarView component. Update localization files for English and German to include translations for calendar features such as views, weekdays, and months. This update improves user experience by providing a comprehensive calendar interface.
This commit is contained in:
@@ -2,7 +2,45 @@
|
|||||||
"personal": {
|
"personal": {
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"title": "Kalender",
|
"title": "Kalender",
|
||||||
"comingSoon": "Der Kalender wird bald verfügbar sein. Hier können Sie zukünftig Ihre Termine und Ereignisse verwalten."
|
"today": "Heute",
|
||||||
|
"views": {
|
||||||
|
"month": "Monat",
|
||||||
|
"week": "Woche",
|
||||||
|
"workweek": "Arbeitswoche",
|
||||||
|
"day": "Tag"
|
||||||
|
},
|
||||||
|
"weekdays": {
|
||||||
|
"mon": "Mo",
|
||||||
|
"tue": "Di",
|
||||||
|
"wed": "Mi",
|
||||||
|
"thu": "Do",
|
||||||
|
"fri": "Fr",
|
||||||
|
"sat": "Sa",
|
||||||
|
"sun": "So"
|
||||||
|
},
|
||||||
|
"weekdaysFull": {
|
||||||
|
"mon": "Montag",
|
||||||
|
"tue": "Dienstag",
|
||||||
|
"wed": "Mittwoch",
|
||||||
|
"thu": "Donnerstag",
|
||||||
|
"fri": "Freitag",
|
||||||
|
"sat": "Samstag",
|
||||||
|
"sun": "Sonntag"
|
||||||
|
},
|
||||||
|
"months": {
|
||||||
|
"jan": "Januar",
|
||||||
|
"feb": "Februar",
|
||||||
|
"mar": "März",
|
||||||
|
"apr": "April",
|
||||||
|
"may": "Mai",
|
||||||
|
"jun": "Juni",
|
||||||
|
"jul": "Juli",
|
||||||
|
"aug": "August",
|
||||||
|
"sep": "September",
|
||||||
|
"oct": "Oktober",
|
||||||
|
"nov": "November",
|
||||||
|
"dec": "Dezember"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,45 @@
|
|||||||
"personal": {
|
"personal": {
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"title": "Calendar",
|
"title": "Calendar",
|
||||||
"comingSoon": "The calendar will be available soon. Here you will be able to manage your appointments and events."
|
"today": "Today",
|
||||||
|
"views": {
|
||||||
|
"month": "Month",
|
||||||
|
"week": "Week",
|
||||||
|
"workweek": "Work Week",
|
||||||
|
"day": "Day"
|
||||||
|
},
|
||||||
|
"weekdays": {
|
||||||
|
"mon": "Mon",
|
||||||
|
"tue": "Tue",
|
||||||
|
"wed": "Wed",
|
||||||
|
"thu": "Thu",
|
||||||
|
"fri": "Fri",
|
||||||
|
"sat": "Sat",
|
||||||
|
"sun": "Sun"
|
||||||
|
},
|
||||||
|
"weekdaysFull": {
|
||||||
|
"mon": "Monday",
|
||||||
|
"tue": "Tuesday",
|
||||||
|
"wed": "Wednesday",
|
||||||
|
"thu": "Thursday",
|
||||||
|
"fri": "Friday",
|
||||||
|
"sat": "Saturday",
|
||||||
|
"sun": "Sunday"
|
||||||
|
},
|
||||||
|
"months": {
|
||||||
|
"jan": "January",
|
||||||
|
"feb": "February",
|
||||||
|
"mar": "March",
|
||||||
|
"apr": "April",
|
||||||
|
"may": "May",
|
||||||
|
"jun": "June",
|
||||||
|
"jul": "July",
|
||||||
|
"aug": "August",
|
||||||
|
"sep": "September",
|
||||||
|
"oct": "October",
|
||||||
|
"nov": "November",
|
||||||
|
"dec": "December"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,144 @@
|
|||||||
<div class="calendar-view">
|
<div class="calendar-view">
|
||||||
<h2>{{ $t('personal.calendar.title') }}</h2>
|
<h2>{{ $t('personal.calendar.title') }}</h2>
|
||||||
|
|
||||||
<div class="coming-soon">
|
<!-- Toolbar -->
|
||||||
<img src="/images/icons/coming-soon.png" alt="Coming Soon" class="coming-soon-icon" />
|
<div class="calendar-toolbar">
|
||||||
<p>{{ $t('personal.calendar.comingSoon') }}</p>
|
<div class="nav-buttons">
|
||||||
|
<button @click="goToToday" class="btn-today">{{ $t('personal.calendar.today') }}</button>
|
||||||
|
<button @click="navigatePrev" class="btn-nav"><</button>
|
||||||
|
<button @click="navigateNext" class="btn-nav">></button>
|
||||||
|
<span class="current-period">{{ currentPeriodLabel }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="view-tabs">
|
||||||
|
<button
|
||||||
|
v-for="view in views"
|
||||||
|
:key="view.id"
|
||||||
|
:class="['view-tab', { active: currentView === view.id }]"
|
||||||
|
@click="currentView = view.id"
|
||||||
|
>
|
||||||
|
{{ $t(`personal.calendar.views.${view.id}`) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Month View -->
|
||||||
|
<div v-if="currentView === 'month'" class="calendar-grid month-view">
|
||||||
|
<div class="weekday-headers">
|
||||||
|
<div v-for="day in weekDays" :key="day" class="weekday-header">
|
||||||
|
{{ $t(`personal.calendar.weekdays.${day}`) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="month-days">
|
||||||
|
<div
|
||||||
|
v-for="(day, index) in monthDays"
|
||||||
|
:key="index"
|
||||||
|
:class="['day-cell', {
|
||||||
|
'other-month': !day.currentMonth,
|
||||||
|
'today': day.isToday,
|
||||||
|
'weekend': day.isWeekend
|
||||||
|
}]"
|
||||||
|
@click="selectDate(day.date)"
|
||||||
|
>
|
||||||
|
<span class="day-number">{{ day.dayNumber }}</span>
|
||||||
|
<div class="day-events">
|
||||||
|
<div
|
||||||
|
v-for="event in getEventsForDate(day.date)"
|
||||||
|
:key="event.id"
|
||||||
|
class="event-dot"
|
||||||
|
:style="{ backgroundColor: event.color }"
|
||||||
|
:title="event.title"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Week View -->
|
||||||
|
<div v-if="currentView === 'week'" class="calendar-grid week-view">
|
||||||
|
<div class="time-grid">
|
||||||
|
<div class="time-header-spacer"></div>
|
||||||
|
<div v-for="day in weekDaysData" :key="day.date" class="day-column-header">
|
||||||
|
<div :class="['day-name', { today: day.isToday }]">
|
||||||
|
{{ $t(`personal.calendar.weekdays.${day.weekday}`) }}
|
||||||
|
</div>
|
||||||
|
<div :class="['day-date', { today: day.isToday }]">{{ day.dayNumber }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time-slots-container">
|
||||||
|
<div class="time-labels">
|
||||||
|
<div v-for="hour in hours" :key="hour" class="time-label">
|
||||||
|
{{ formatHour(hour) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="day-columns">
|
||||||
|
<div v-for="day in weekDaysData" :key="day.date" class="day-column">
|
||||||
|
<div
|
||||||
|
v-for="hour in hours"
|
||||||
|
:key="hour"
|
||||||
|
:class="['time-slot', { 'current-hour': isCurrentHour(day.date, hour) }]"
|
||||||
|
@click="createEventAt(day.date, hour)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Work Week View -->
|
||||||
|
<div v-if="currentView === 'workweek'" class="calendar-grid week-view">
|
||||||
|
<div class="time-grid">
|
||||||
|
<div class="time-header-spacer"></div>
|
||||||
|
<div v-for="day in workWeekDaysData" :key="day.date" class="day-column-header">
|
||||||
|
<div :class="['day-name', { today: day.isToday }]">
|
||||||
|
{{ $t(`personal.calendar.weekdays.${day.weekday}`) }}
|
||||||
|
</div>
|
||||||
|
<div :class="['day-date', { today: day.isToday }]">{{ day.dayNumber }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time-slots-container">
|
||||||
|
<div class="time-labels">
|
||||||
|
<div v-for="hour in workHours" :key="hour" class="time-label">
|
||||||
|
{{ formatHour(hour) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="day-columns">
|
||||||
|
<div v-for="day in workWeekDaysData" :key="day.date" class="day-column">
|
||||||
|
<div
|
||||||
|
v-for="hour in workHours"
|
||||||
|
:key="hour"
|
||||||
|
:class="['time-slot', { 'current-hour': isCurrentHour(day.date, hour) }]"
|
||||||
|
@click="createEventAt(day.date, hour)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Day View -->
|
||||||
|
<div v-if="currentView === 'day'" class="calendar-grid day-view">
|
||||||
|
<div class="day-header">
|
||||||
|
<div :class="['day-title', { today: isDayToday }]">
|
||||||
|
{{ $t(`personal.calendar.weekdays.${currentDayData.weekday}`) }},
|
||||||
|
{{ currentDayData.dayNumber }}. {{ $t(`personal.calendar.months.${currentDayData.month}`) }} {{ currentDayData.year }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time-slots-container single-day">
|
||||||
|
<div class="time-labels">
|
||||||
|
<div v-for="hour in hours" :key="hour" class="time-label">
|
||||||
|
{{ formatHour(hour) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="day-columns">
|
||||||
|
<div class="day-column full-width">
|
||||||
|
<div
|
||||||
|
v-for="hour in hours"
|
||||||
|
:key="hour"
|
||||||
|
:class="['time-slot', { 'current-hour': isCurrentHour(currentDate, hour) }]"
|
||||||
|
@click="createEventAt(currentDate, hour)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -12,12 +147,184 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'CalendarView',
|
name: 'CalendarView',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentView: 'month',
|
||||||
|
currentDate: new Date(),
|
||||||
|
views: [
|
||||||
|
{ id: 'month' },
|
||||||
|
{ id: 'week' },
|
||||||
|
{ id: 'workweek' },
|
||||||
|
{ id: 'day' }
|
||||||
|
],
|
||||||
|
weekDays: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
|
||||||
|
hours: Array.from({ length: 24 }, (_, i) => i),
|
||||||
|
workHours: Array.from({ length: 12 }, (_, i) => i + 7), // 7:00 - 18:00
|
||||||
|
events: [] // Placeholder for events
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentPeriodLabel() {
|
||||||
|
const options = { month: 'long', year: 'numeric' };
|
||||||
|
if (this.currentView === 'day') {
|
||||||
|
options.day = 'numeric';
|
||||||
|
}
|
||||||
|
return this.currentDate.toLocaleDateString(this.$i18n.locale, options);
|
||||||
|
},
|
||||||
|
monthDays() {
|
||||||
|
const year = this.currentDate.getFullYear();
|
||||||
|
const month = this.currentDate.getMonth();
|
||||||
|
const firstDay = new Date(year, month, 1);
|
||||||
|
const lastDay = new Date(year, month + 1, 0);
|
||||||
|
|
||||||
|
// Find Monday of the first week
|
||||||
|
let startDate = new Date(firstDay);
|
||||||
|
const dayOfWeek = firstDay.getDay();
|
||||||
|
const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
|
||||||
|
startDate.setDate(startDate.getDate() + diff);
|
||||||
|
|
||||||
|
const days = [];
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
// Generate 6 weeks (42 days)
|
||||||
|
for (let i = 0; i < 42; i++) {
|
||||||
|
const date = new Date(startDate);
|
||||||
|
date.setDate(startDate.getDate() + i);
|
||||||
|
const dayOfWeekNum = date.getDay();
|
||||||
|
|
||||||
|
days.push({
|
||||||
|
date: date.toISOString().split('T')[0],
|
||||||
|
dayNumber: date.getDate(),
|
||||||
|
currentMonth: date.getMonth() === month,
|
||||||
|
isToday: date.getTime() === today.getTime(),
|
||||||
|
isWeekend: dayOfWeekNum === 0 || dayOfWeekNum === 6
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return days;
|
||||||
|
},
|
||||||
|
weekDaysData() {
|
||||||
|
const days = [];
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
// Find Monday of current week
|
||||||
|
const monday = this.getMonday(this.currentDate);
|
||||||
|
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const date = new Date(monday);
|
||||||
|
date.setDate(monday.getDate() + i);
|
||||||
|
const dayOfWeek = date.getDay();
|
||||||
|
|
||||||
|
days.push({
|
||||||
|
date: date.toISOString().split('T')[0],
|
||||||
|
dayNumber: date.getDate(),
|
||||||
|
weekday: this.weekDays[i],
|
||||||
|
isToday: date.getTime() === today.getTime(),
|
||||||
|
isWeekend: dayOfWeek === 0 || dayOfWeek === 6
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return days;
|
||||||
|
},
|
||||||
|
workWeekDaysData() {
|
||||||
|
// Only Monday to Friday
|
||||||
|
return this.weekDaysData.slice(0, 5);
|
||||||
|
},
|
||||||
|
currentDayData() {
|
||||||
|
const date = this.currentDate;
|
||||||
|
const dayIndex = date.getDay();
|
||||||
|
// Convert Sunday (0) to index 6, Monday (1) to 0, etc.
|
||||||
|
const weekdayIndex = dayIndex === 0 ? 6 : dayIndex - 1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: date.toISOString().split('T')[0],
|
||||||
|
dayNumber: date.getDate(),
|
||||||
|
month: this.getMonthKey(date.getMonth()),
|
||||||
|
year: date.getFullYear(),
|
||||||
|
weekday: this.weekDays[weekdayIndex]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
isDayToday() {
|
||||||
|
const today = new Date();
|
||||||
|
return this.currentDate.toDateString() === today.toDateString();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getMonday(date) {
|
||||||
|
const d = new Date(date);
|
||||||
|
const day = d.getDay();
|
||||||
|
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
||||||
|
return new Date(d.setDate(diff));
|
||||||
|
},
|
||||||
|
getMonthKey(monthIndex) {
|
||||||
|
const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
||||||
|
return months[monthIndex];
|
||||||
|
},
|
||||||
|
navigatePrev() {
|
||||||
|
const newDate = new Date(this.currentDate);
|
||||||
|
switch (this.currentView) {
|
||||||
|
case 'month':
|
||||||
|
newDate.setMonth(newDate.getMonth() - 1);
|
||||||
|
break;
|
||||||
|
case 'week':
|
||||||
|
case 'workweek':
|
||||||
|
newDate.setDate(newDate.getDate() - 7);
|
||||||
|
break;
|
||||||
|
case 'day':
|
||||||
|
newDate.setDate(newDate.getDate() - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.currentDate = newDate;
|
||||||
|
},
|
||||||
|
navigateNext() {
|
||||||
|
const newDate = new Date(this.currentDate);
|
||||||
|
switch (this.currentView) {
|
||||||
|
case 'month':
|
||||||
|
newDate.setMonth(newDate.getMonth() + 1);
|
||||||
|
break;
|
||||||
|
case 'week':
|
||||||
|
case 'workweek':
|
||||||
|
newDate.setDate(newDate.getDate() + 7);
|
||||||
|
break;
|
||||||
|
case 'day':
|
||||||
|
newDate.setDate(newDate.getDate() + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.currentDate = newDate;
|
||||||
|
},
|
||||||
|
goToToday() {
|
||||||
|
this.currentDate = new Date();
|
||||||
|
},
|
||||||
|
formatHour(hour) {
|
||||||
|
return `${hour.toString().padStart(2, '0')}:00`;
|
||||||
|
},
|
||||||
|
isCurrentHour(dateStr, hour) {
|
||||||
|
const now = new Date();
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
return date.toDateString() === now.toDateString() && now.getHours() === hour;
|
||||||
|
},
|
||||||
|
selectDate(dateStr) {
|
||||||
|
this.currentDate = new Date(dateStr);
|
||||||
|
this.currentView = 'day';
|
||||||
|
},
|
||||||
|
createEventAt(dateStr, hour) {
|
||||||
|
// Placeholder for event creation
|
||||||
|
console.log(`Create event at ${dateStr} ${hour}:00`);
|
||||||
|
},
|
||||||
|
getEventsForDate(dateStr) {
|
||||||
|
return this.events.filter(e => e.date === dateStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.calendar-view {
|
.calendar-view {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@@ -25,27 +332,333 @@ h2 {
|
|||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.coming-soon {
|
// Toolbar
|
||||||
|
.calendar-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
padding: 60px 20px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border: 1px dashed #ccc;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.coming-soon-icon {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
opacity: 0.7;
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.coming-soon p {
|
.nav-buttons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-today {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: var(--color-primary-orange);
|
||||||
|
color: #000;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-primary-orange-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-nav {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.1em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-period {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 10px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-tab {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
color: #666;
|
color: #666;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--color-primary-orange);
|
||||||
|
color: #000;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Month View
|
||||||
|
.month-view {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekday-headers {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
background: #f8f8f8;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekday-header {
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.month-days {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-cell {
|
||||||
|
min-height: 80px;
|
||||||
|
padding: 8px;
|
||||||
|
border-right: 1px solid #eee;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:nth-child(7n) {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-primary-orange-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.other-month {
|
||||||
|
background: #fafafa;
|
||||||
|
|
||||||
|
.day-number {
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
background: #fff8e1;
|
||||||
|
|
||||||
|
.day-number {
|
||||||
|
background: var(--color-primary-orange);
|
||||||
|
color: #000;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.weekend {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-number {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.95em;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-events {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Week & Day View
|
||||||
|
.week-view, .day-view {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-grid {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
background: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-header-spacer {
|
||||||
|
width: 60px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-column-header {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-align: center;
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
|
||||||
|
&:first-of-type {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.85em;
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
color: var(--color-primary-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-date {
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
background: var(--color-primary-orange);
|
||||||
|
color: #000;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 4px auto 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-slots-container {
|
||||||
|
display: flex;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&.single-day {
|
||||||
|
.day-column {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-labels {
|
||||||
|
width: 60px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label {
|
||||||
|
height: 48px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #888;
|
||||||
|
text-align: right;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-columns {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-column {
|
||||||
|
flex: 1;
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-slot {
|
||||||
|
height: 48px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-primary-orange-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current-hour {
|
||||||
|
background: rgba(255, 184, 77, 0.2);
|
||||||
|
border-left: 3px solid var(--color-primary-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Day View specifics
|
||||||
|
.day-header {
|
||||||
|
padding: 20px;
|
||||||
|
background: #f8f8f8;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
color: var(--color-primary-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.calendar-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-buttons {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-tabs {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-cell {
|
||||||
|
min-height: 60px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekday-header {
|
||||||
|
padding: 8px 4px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user