[#1] added base calendar and started to tweak ui
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar{
|
||||
border-right: 2px gray;
|
||||
}
|
||||
|
||||
.fc .fc-daygrid-day {
|
||||
min-height: 10px !important; /* Default is ~40px */
|
||||
height: 10px !important;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.fc .fc-timegrid-slot {
|
||||
height: 60px !important; /* Default is ~40px */
|
||||
}
|
||||
|
||||
#calendar {
|
||||
flex: 1;
|
||||
overflow: hidden; /* Important: let FullCalendar handle internal scroll */
|
||||
}
|
||||
|
||||
.fc-scroller-liquid-absolute {
|
||||
overflow-y: auto !important; /* This targets the scrollable part */
|
||||
max-height: calc(100vh - 100px); /* Adjust based on your header size */
|
||||
}
|
||||
.scrollgrid-section-header {
|
||||
border: white;
|
||||
}
|
||||
|
||||
/* Base container */
|
||||
.fc-daygrid-day-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
font-family: "Inter", sans-serif;
|
||||
color: #1e293b; /* dark navy */
|
||||
}
|
||||
|
||||
/* Day number */
|
||||
.fc-daygrid-day-top .fc-daygrid-day-number {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
/* Day name (e.g., MON) */
|
||||
.fc-daygrid-day-top small {
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.05em;
|
||||
color: #94a3b8; /* slate gray */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Today’s date highlight */
|
||||
.current-date {
|
||||
background-color: orange;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.fc .fc-col-header-cell,
|
||||
.fc .fc-scrollgrid table,
|
||||
.fc-theme-standard .fc-scrollgrid {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Container de la sidebar */
|
||||
.sidebar-calendar {
|
||||
width: 280px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
/* Style du mini calendrier */
|
||||
.react-calendar {
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Jours sélectionnés et hover */
|
||||
.react-calendar__tile {
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background-color: white;
|
||||
width: 12px;
|
||||
height: 37.52px;
|
||||
}
|
||||
|
||||
.react-calendar__tile:hover {
|
||||
background-color: rgb(224, 224, 224);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.react-calendar__tile--active {
|
||||
background-color: orange;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Titre ou header de la sidebar */
|
||||
.sidebar-calendar h2 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
/* Liste des calendriers */
|
||||
.calendar-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.calendar-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 600;
|
||||
color: #444;
|
||||
padding: 10px 15px;
|
||||
border-radius: 10px;
|
||||
transition: background-color 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Couleurs distinctes pour chaque type de calendrier */
|
||||
.calendar-list li.work {
|
||||
background-color: #d0e8ff;
|
||||
color: #1a73e8;
|
||||
}
|
||||
|
||||
.calendar-list li.work:hover {
|
||||
background-color: #b0d4ff;
|
||||
}
|
||||
|
||||
.calendar-list li.personal {
|
||||
background-color: #ffe0e0;
|
||||
color: #d93025;
|
||||
}
|
||||
|
||||
.calendar-list li.personal:hover {
|
||||
background-color: #ffb3b3;
|
||||
}
|
||||
|
||||
/* Petit indicateur couleur à gauche */
|
||||
.calendar-list li::before {
|
||||
content: "";
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.calendar-list li.work::before {
|
||||
background-color: #1a73e8;
|
||||
}
|
||||
|
||||
.calendar-list li.personal::before {
|
||||
background-color: #d93025;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import FullCalendar from "@fullcalendar/react";
|
||||
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
import { CalendarApi, DateSelectArg } from "@fullcalendar/core";
|
||||
import ReactCalendar from "react-calendar";
|
||||
import "./Calendar.css";
|
||||
import { useRef, useState } from "react";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
|
||||
export default function CalendarApp() {
|
||||
const calendarRef = useRef<CalendarApi | null>(null);
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
const [selectedCalendars, setSelectedCalendars] = useState([
|
||||
"Work",
|
||||
"Personnal",
|
||||
]);
|
||||
const events = useAppSelector((state) => state.events);
|
||||
|
||||
const handleCalendarToggle = (name: string) => {
|
||||
setSelectedCalendars((prev) =>
|
||||
prev.includes(name) ? prev.filter((n) => n !== name) : [...prev, name]
|
||||
);
|
||||
};
|
||||
const filteredEvent = events.filter((e) =>
|
||||
selectedCalendars.includes(e.calendar)
|
||||
);
|
||||
|
||||
const [date, setDate] = useState(new Date());
|
||||
|
||||
const months = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="sidebar">
|
||||
<div className="calendar-label">
|
||||
<div className="calendar-label">
|
||||
<span>
|
||||
{months[date.getMonth()]} {date.getFullYear()}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() =>
|
||||
setDate(new Date(date.getFullYear(), date.getMonth() - 1))
|
||||
}
|
||||
>
|
||||
<
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setDate(new Date(date.getFullYear(), date.getMonth() + 1))
|
||||
}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<ReactCalendar
|
||||
showNeighboringMonth={false}
|
||||
calendarType="hebrew"
|
||||
formatShortWeekday={(locale, date) =>
|
||||
date.toLocaleDateString(locale, { weekday: "narrow" })
|
||||
}
|
||||
value={selectedDate}
|
||||
onClickDay={(date) => {
|
||||
setSelectedDate(date);
|
||||
calendarRef.current?.gotoDate(date);
|
||||
}}
|
||||
prevLabel={null}
|
||||
nextLabel={null}
|
||||
showNavigation={false}
|
||||
/>
|
||||
{["Work", "Personnal", "Holidays"].map((name) => (
|
||||
<div key={name}>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedCalendars.includes(name)}
|
||||
onChange={() => handleCalendarToggle(name)}
|
||||
/>
|
||||
{name}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="calendar">
|
||||
<FullCalendar
|
||||
ref={(ref) => {
|
||||
if (ref) {
|
||||
calendarRef.current = ref.getApi();
|
||||
}
|
||||
}}
|
||||
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
|
||||
initialView="timeGridWeek"
|
||||
weekends={false}
|
||||
editable={true}
|
||||
selectable={true}
|
||||
nowIndicator
|
||||
selectMirror={true}
|
||||
views={{
|
||||
timeGridWeek: { titleFormat: { month: "long", year: "numeric" } },
|
||||
}}
|
||||
dayMaxEvents={true}
|
||||
events={filteredEvent}
|
||||
weekNumbers
|
||||
weekNumberFormat={{ week: "long" }}
|
||||
slotDuration={"00:30:00"}
|
||||
slotLabelInterval={"00:30:00"}
|
||||
scrollTime={"07:00:00"}
|
||||
allDayText=""
|
||||
slotLabelFormat={{
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
}}
|
||||
dayHeaderContent={(arg) => {
|
||||
const date = arg.date.getDate();
|
||||
const weekDay = arg.date
|
||||
.toLocaleDateString("en-US", { weekday: "short" })
|
||||
.toUpperCase();
|
||||
return (
|
||||
<div className="fc-daygrid-day-top">
|
||||
<small>{weekDay}</small>
|
||||
<span
|
||||
className={`fc-daygrid-day-number ${
|
||||
arg.isToday ? "current-date" : ""
|
||||
}`}
|
||||
>
|
||||
{date}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
eventClick={(info) => {
|
||||
info.jsEvent.preventDefault(); // don't let the browser navigate
|
||||
|
||||
if (info.event.url) {
|
||||
window.open(info.event.url);
|
||||
} else {
|
||||
console.log(info.event);
|
||||
}
|
||||
}}
|
||||
headerToolbar={{
|
||||
left: "title",
|
||||
center: "prev,today,next",
|
||||
right: "dayGridMonth,timeGridWeek,timeGridDay",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
import logo from "../../static/images/calendar.svg";
|
||||
|
||||
export function Error() {
|
||||
return <p>Error</p>;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
import logo from "../../static/images/calendar.svg";
|
||||
|
||||
export function Loading() {
|
||||
return <img src={logo} />;
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
.calendar-text {
|
||||
font-family: "Cal Sans";
|
||||
padding: 0.2rem;
|
||||
font-weight: 400;
|
||||
@@ -39,12 +39,8 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #1a1a1a62;
|
||||
padding: 0.5rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +1,27 @@
|
||||
import React from "react";
|
||||
import { Auth } from "../../features/User/oidcAuth";
|
||||
import logo from "../../static/images/calendar.svg";
|
||||
import "./Menubar.css";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
export function Menubar() {
|
||||
return (
|
||||
<div className="menubar">
|
||||
<div className="menubar-item tc-home">
|
||||
<img className="logo" src={logo} />
|
||||
<p>
|
||||
<span className="twake">Twake</span>
|
||||
<span className="calendar">Calendar</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="menubar-item nav-month">
|
||||
<p>Current Month</p>
|
||||
<p className="day-selector"> droite today gauche</p>
|
||||
</div>
|
||||
<header className="menubar">
|
||||
<MainTitle/>
|
||||
<div className="menubar-item search-bar">
|
||||
<p>big search bar</p>
|
||||
</div>
|
||||
<div className="menubar-item menu-tools">
|
||||
<HandleLogin />
|
||||
<div className="menubar-item">
|
||||
<p>Account stuff</p>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
export function MainTitle() {
|
||||
return (
|
||||
<div className="menubar-item tc-home">
|
||||
<img className="logo" src={logo} />
|
||||
<p>
|
||||
<span className="twake">Twake</span>
|
||||
<span className="calendar-text">Calendar</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HandleLogin() {
|
||||
const userData = useAppSelector((state) => state.user.userData);
|
||||
if (!userData) {
|
||||
return (
|
||||
<button
|
||||
onClick={async () => {
|
||||
const loginurl = await Auth();
|
||||
sessionStorage.setItem(
|
||||
"redirectState",
|
||||
JSON.stringify({
|
||||
code_verifier: loginurl.code_verifier,
|
||||
state: loginurl.state,
|
||||
})
|
||||
);
|
||||
|
||||
window.location.assign(loginurl.redirectTo);
|
||||
}}
|
||||
>
|
||||
login
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
return <p>{userData.sub}</p>;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user