mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-04 00:10:57 +00:00 
			
		
		
		
	Feature: Added agenda view for calendar, calendar improvements (#2216)
* Feature: Added agenda view for calendar, calendar improvements * Fix duplicate event keys * Additional hover on title, not date * Show date once in list * Rename monthly view for consistency * Remove unneeded key props * CSS cleanup, dont slice title to arbitrary 42 chars which can break column layouts * Simplify agenda components * Fix show date once in list --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									792f768a7f
								
							
						
					
					
						commit
						6898faa3de
					
				@ -3,6 +3,8 @@ title: Calendar
 | 
				
			|||||||
description: Calendar widget
 | 
					description: Calendar widget
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Monthly view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<img alt="calendar" src="https://user-images.githubusercontent.com/5442891/271131282-6767a3ea-573e-4005-aeb9-6e14ee01e845.png">
 | 
					<img alt="calendar" src="https://user-images.githubusercontent.com/5442891/271131282-6767a3ea-573e-4005-aeb9-6e14ee01e845.png">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This widget shows monthly calendar, with optional integrations to show events from supported widgets.
 | 
					This widget shows monthly calendar, with optional integrations to show events from supported widgets.
 | 
				
			||||||
@ -11,6 +13,8 @@ This widget shows monthly calendar, with optional integrations to show events fr
 | 
				
			|||||||
widget:
 | 
					widget:
 | 
				
			||||||
  type: calendar
 | 
					  type: calendar
 | 
				
			||||||
  firstDayInWeek: sunday # optional - defaults to monday
 | 
					  firstDayInWeek: sunday # optional - defaults to monday
 | 
				
			||||||
 | 
					  view: monthly # optional - possible values monthly, agenda
 | 
				
			||||||
 | 
					  maxEvents: 10 # optional - defaults to 10
 | 
				
			||||||
  integrations: # optional
 | 
					  integrations: # optional
 | 
				
			||||||
    - type: sonarr # active widget type that is currently enabled on homepage - possible values: radarr, sonarr, lidarr, readarr
 | 
					    - type: sonarr # active widget type that is currently enabled on homepage - possible values: radarr, sonarr, lidarr, readarr
 | 
				
			||||||
      service_group: Media # group name where widget exists
 | 
					      service_group: Media # group name where widget exists
 | 
				
			||||||
@ -20,6 +24,20 @@ widget:
 | 
				
			|||||||
        unmonitored: true # optional - defaults to false, used with *arr stack
 | 
					        unmonitored: true # optional - defaults to false, used with *arr stack
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Agenda
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This view shows only list of events from configured integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					widget:
 | 
				
			||||||
 | 
					  type: calendar
 | 
				
			||||||
 | 
					  view: agenda
 | 
				
			||||||
 | 
					  maxEvents: 10 # optional - defaults to 10
 | 
				
			||||||
 | 
					  integrations: # same as in Monthly view example
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Currently integrated widgets are [sonarr](sonarr.md), [radarr](radarr.md), [lidarr](lidarr.md) and [readarr](readarr.md).
 | 
					Currently integrated widgets are [sonarr](sonarr.md), [radarr](radarr.md), [lidarr](lidarr.md) and [readarr](readarr.md).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Supported colors can be found on [color palette](../../configs/settings.md#color-palette).
 | 
					Supported colors can be found on [color palette](../../configs/settings.md#color-palette).
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ import { ColorProvider } from "utils/contexts/color";
 | 
				
			|||||||
import { ThemeProvider } from "utils/contexts/theme";
 | 
					import { ThemeProvider } from "utils/contexts/theme";
 | 
				
			||||||
import { SettingsProvider } from "utils/contexts/settings";
 | 
					import { SettingsProvider } from "utils/contexts/settings";
 | 
				
			||||||
import { TabProvider } from "utils/contexts/tab";
 | 
					import { TabProvider } from "utils/contexts/tab";
 | 
				
			||||||
import { EventProvider, ShowDateProvider } from "utils/contexts/calendar";
 | 
					import { EventProvider } from "utils/contexts/calendar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function MyApp({ Component, pageProps }) {
 | 
					function MyApp({ Component, pageProps }) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
@ -33,9 +33,7 @@ function MyApp({ Component, pageProps }) {
 | 
				
			|||||||
          <SettingsProvider>
 | 
					          <SettingsProvider>
 | 
				
			||||||
            <TabProvider>
 | 
					            <TabProvider>
 | 
				
			||||||
              <EventProvider>
 | 
					              <EventProvider>
 | 
				
			||||||
                <ShowDateProvider>
 | 
					                <Component {...pageProps} />
 | 
				
			||||||
                  <Component {...pageProps} />
 | 
					 | 
				
			||||||
                </ShowDateProvider>
 | 
					 | 
				
			||||||
              </EventProvider>
 | 
					              </EventProvider>
 | 
				
			||||||
            </TabProvider>
 | 
					            </TabProvider>
 | 
				
			||||||
          </SettingsProvider>
 | 
					          </SettingsProvider>
 | 
				
			||||||
 | 
				
			|||||||
@ -364,6 +364,8 @@ export function cleanServiceGroups(groups) {
 | 
				
			|||||||
          refreshInterval,
 | 
					          refreshInterval,
 | 
				
			||||||
          integrations, // calendar widget
 | 
					          integrations, // calendar widget
 | 
				
			||||||
          firstDayInWeek,
 | 
					          firstDayInWeek,
 | 
				
			||||||
 | 
					          view,
 | 
				
			||||||
 | 
					          maxEvents,
 | 
				
			||||||
        } = cleanedService.widget;
 | 
					        } = cleanedService.widget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let fieldsList = fields;
 | 
					        let fieldsList = fields;
 | 
				
			||||||
@ -450,6 +452,8 @@ export function cleanServiceGroups(groups) {
 | 
				
			|||||||
        if (type === "calendar") {
 | 
					        if (type === "calendar") {
 | 
				
			||||||
          if (integrations) cleanedService.widget.integrations = integrations;
 | 
					          if (integrations) cleanedService.widget.integrations = integrations;
 | 
				
			||||||
          if (firstDayInWeek) cleanedService.widget.firstDayInWeek = firstDayInWeek;
 | 
					          if (firstDayInWeek) cleanedService.widget.firstDayInWeek = firstDayInWeek;
 | 
				
			||||||
 | 
					          if (view) cleanedService.widget.view = view;
 | 
				
			||||||
 | 
					          if (maxEvents) cleanedService.widget.maxEvents = maxEvents;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
import { createContext, useState, useMemo } from "react";
 | 
					import { createContext, useState, useMemo } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const EventContext = createContext();
 | 
					export const EventContext = createContext();
 | 
				
			||||||
export const ShowDateContext = createContext();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function EventProvider({ initialEvent, children }) {
 | 
					export function EventProvider({ initialEvent, children }) {
 | 
				
			||||||
  const [events, setEvents] = useState({});
 | 
					  const [events, setEvents] = useState({});
 | 
				
			||||||
@ -14,15 +13,3 @@ export function EventProvider({ initialEvent, children }) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return <EventContext.Provider value={value}>{children}</EventContext.Provider>;
 | 
					  return <EventContext.Provider value={value}>{children}</EventContext.Provider>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function ShowDateProvider({ initialDate, children }) {
 | 
					 | 
				
			||||||
  const [showDate, setShowDate] = useState(null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (initialDate) {
 | 
					 | 
				
			||||||
    setShowDate(initialDate);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const value = useMemo(() => ({ showDate, setShowDate }), [showDate]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return <ShowDateContext.Provider value={value}>{children}</ShowDateContext.Provider>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										101
									
								
								src/widgets/calendar/agenda.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/widgets/calendar/agenda.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					import { useContext, useState } from "react";
 | 
				
			||||||
 | 
					import { DateTime } from "luxon";
 | 
				
			||||||
 | 
					import classNames from "classnames";
 | 
				
			||||||
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					import { IoMdCheckmarkCircleOutline } from "react-icons/io";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { EventContext } from "../../utils/contexts/calendar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function Event({ event, colorVariants, showDate = false }) {
 | 
				
			||||||
 | 
					  const [hover, setHover] = useState(false);
 | 
				
			||||||
 | 
					  const { i18n } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      className="flex flex-row text-theme-700 dark:text-theme-200 items-center text-xs text-left h-5 rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1"
 | 
				
			||||||
 | 
					      onMouseEnter={() => setHover(!hover)}
 | 
				
			||||||
 | 
					      onMouseLeave={() => setHover(!hover)}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <span className="ml-2 w-10">
 | 
				
			||||||
 | 
					        <span>
 | 
				
			||||||
 | 
					          {showDate &&
 | 
				
			||||||
 | 
					            event.date.setLocale(i18n.language).startOf("day").toLocaleString({ month: "short", day: "numeric" })}
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					      <span className="ml-2 h-2 w-2">
 | 
				
			||||||
 | 
					        <span className={classNames("block w-2 h-2 rounded", colorVariants[event.color] ?? "gray")} />
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					      <div className="ml-2 h-5 text-left relative truncate" style={{ width: "70%" }}>
 | 
				
			||||||
 | 
					        <div className="absolute mt-0.5 text-xs">{hover && event.additional ? event.additional : event.title}</div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      {event.isCompleted && (
 | 
				
			||||||
 | 
					        <span className="text-xs mr-1 ml-auto z-10">
 | 
				
			||||||
 | 
					          <IoMdCheckmarkCircleOutline />
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Agenda({ service, colorVariants, showDate }) {
 | 
				
			||||||
 | 
					  const { widget } = service;
 | 
				
			||||||
 | 
					  const { events } = useContext(EventContext);
 | 
				
			||||||
 | 
					  const { i18n } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!showDate) {
 | 
				
			||||||
 | 
					    return <div className=" text-center" />;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const eventsArray = Object.keys(events)
 | 
				
			||||||
 | 
					    .filter(
 | 
				
			||||||
 | 
					      (eventKey) => showDate.startOf("day").toUnixInteger() <= events[eventKey].date?.startOf("day").toUnixInteger(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .map((eventKey) => events[eventKey])
 | 
				
			||||||
 | 
					    .sort((a, b) => a.date - b.date)
 | 
				
			||||||
 | 
					    .slice(0, widget?.maxEvents ?? 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!eventsArray.length) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="text-center">
 | 
				
			||||||
 | 
					        <div className="p-2 ">
 | 
				
			||||||
 | 
					          <div
 | 
				
			||||||
 | 
					            className={classNames("flex flex-col pt-1 pb-1", !eventsArray.length && !events.length && "animate-pulse")}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Event
 | 
				
			||||||
 | 
					              key="no-event"
 | 
				
			||||||
 | 
					              event={{
 | 
				
			||||||
 | 
					                title: `No events for today!`,
 | 
				
			||||||
 | 
					                date: DateTime.now(),
 | 
				
			||||||
 | 
					                color: "gray",
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              colorVariants={colorVariants}
 | 
				
			||||||
 | 
					              i18n={i18n}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const days = Array.from(new Set(eventsArray.map((e) => e.date.startOf("day").ts)));
 | 
				
			||||||
 | 
					  const eventsByDay = days.map((d) => eventsArray.filter((e) => e.date.startOf("day").ts === d));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="p-2">
 | 
				
			||||||
 | 
					      <div className={classNames("flex flex-col pt-1 pb-1", !eventsArray.length && !events.length && "animate-pulse")}>
 | 
				
			||||||
 | 
					        {eventsByDay.map((eventsDay, i) => (
 | 
				
			||||||
 | 
					          <div key={days[i]}>
 | 
				
			||||||
 | 
					            {eventsDay.map((event, j) => (
 | 
				
			||||||
 | 
					              <Event
 | 
				
			||||||
 | 
					                key={`event${event.title}-${event.date}`}
 | 
				
			||||||
 | 
					                event={event}
 | 
				
			||||||
 | 
					                colorVariants={colorVariants}
 | 
				
			||||||
 | 
					                showDate={j === 0}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            ))}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        ))}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,15 +1,51 @@
 | 
				
			|||||||
import { useContext, useMemo } from "react";
 | 
					import { useEffect, useMemo, useState } from "react";
 | 
				
			||||||
import dynamic from "next/dynamic";
 | 
					import dynamic from "next/dynamic";
 | 
				
			||||||
 | 
					import { DateTime } from "luxon";
 | 
				
			||||||
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ShowDateContext } from "../../utils/contexts/calendar";
 | 
					import Monthly from "./monthly";
 | 
				
			||||||
 | 
					import Agenda from "./agenda";
 | 
				
			||||||
import MonthlyView from "./monthly-view";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Container from "components/services/widget/container";
 | 
					import Container from "components/services/widget/container";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const colorVariants = {
 | 
				
			||||||
 | 
					  // https://tailwindcss.com/docs/content-configuration#dynamic-class-names
 | 
				
			||||||
 | 
					  amber: "bg-amber-500",
 | 
				
			||||||
 | 
					  blue: "bg-blue-500",
 | 
				
			||||||
 | 
					  cyan: "bg-cyan-500",
 | 
				
			||||||
 | 
					  emerald: "bg-emerald-500",
 | 
				
			||||||
 | 
					  fuchsia: "bg-fuchsia-500",
 | 
				
			||||||
 | 
					  gray: "bg-gray-500",
 | 
				
			||||||
 | 
					  green: "bg-green-500",
 | 
				
			||||||
 | 
					  indigo: "bg-indigo-500",
 | 
				
			||||||
 | 
					  lime: "bg-lime-500",
 | 
				
			||||||
 | 
					  neutral: "bg-neutral-500",
 | 
				
			||||||
 | 
					  orange: "bg-orange-500",
 | 
				
			||||||
 | 
					  pink: "bg-pink-500",
 | 
				
			||||||
 | 
					  purple: "bg-purple-500",
 | 
				
			||||||
 | 
					  red: "bg-red-500",
 | 
				
			||||||
 | 
					  rose: "bg-rose-500",
 | 
				
			||||||
 | 
					  sky: "bg-sky-500",
 | 
				
			||||||
 | 
					  slate: "bg-slate-500",
 | 
				
			||||||
 | 
					  stone: "bg-stone-500",
 | 
				
			||||||
 | 
					  teal: "bg-teal-500",
 | 
				
			||||||
 | 
					  violet: "bg-violet-500",
 | 
				
			||||||
 | 
					  white: "bg-white-500",
 | 
				
			||||||
 | 
					  yellow: "bg-yellow-500",
 | 
				
			||||||
 | 
					  zinc: "bg-zinc-500",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Component({ service }) {
 | 
					export default function Component({ service }) {
 | 
				
			||||||
  const { widget } = service;
 | 
					  const { widget } = service;
 | 
				
			||||||
  const { showDate } = useContext(ShowDateContext);
 | 
					  const { i18n } = useTranslation();
 | 
				
			||||||
 | 
					  const [showDate, setShowDate] = useState(null);
 | 
				
			||||||
 | 
					  const currentDate = DateTime.now().setLocale(i18n.language).startOf("day");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!showDate) {
 | 
				
			||||||
 | 
					      setShowDate(currentDate);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [showDate, currentDate]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // params for API fetch
 | 
					  // params for API fetch
 | 
				
			||||||
  const params = useMemo(() => {
 | 
					  const params = useMemo(() => {
 | 
				
			||||||
@ -27,10 +63,12 @@ export default function Component({ service }) {
 | 
				
			|||||||
  // Load active integrations
 | 
					  // Load active integrations
 | 
				
			||||||
  const integrations = useMemo(
 | 
					  const integrations = useMemo(
 | 
				
			||||||
    () =>
 | 
					    () =>
 | 
				
			||||||
      widget.integrations?.map((integration) => ({
 | 
					      widget.integrations
 | 
				
			||||||
        service: dynamic(() => import(`./integrations/${integration?.type}`)),
 | 
					        ?.filter((integration) => integration?.type)
 | 
				
			||||||
        widget: integration,
 | 
					        .map((integration) => ({
 | 
				
			||||||
      })) ?? [],
 | 
					          service: dynamic(() => import(`./integrations/${integration.type}`)),
 | 
				
			||||||
 | 
					          widget: integration,
 | 
				
			||||||
 | 
					        })) ?? [],
 | 
				
			||||||
    [widget.integrations],
 | 
					    [widget.integrations],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,7 +90,24 @@ export default function Component({ service }) {
 | 
				
			|||||||
            );
 | 
					            );
 | 
				
			||||||
          })}
 | 
					          })}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <MonthlyView service={service} className="flex" />
 | 
					        {(!widget?.view || widget?.view === "monthly") && (
 | 
				
			||||||
 | 
					          <Monthly
 | 
				
			||||||
 | 
					            service={service}
 | 
				
			||||||
 | 
					            colorVariants={colorVariants}
 | 
				
			||||||
 | 
					            showDate={showDate}
 | 
				
			||||||
 | 
					            setShowDate={setShowDate}
 | 
				
			||||||
 | 
					            className="flex"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        {widget?.view === "agenda" && (
 | 
				
			||||||
 | 
					          <Agenda
 | 
				
			||||||
 | 
					            service={service}
 | 
				
			||||||
 | 
					            colorVariants={colorVariants}
 | 
				
			||||||
 | 
					            showDate={showDate}
 | 
				
			||||||
 | 
					            setShowDate={setShowDate}
 | 
				
			||||||
 | 
					            className="flex"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </Container>
 | 
					    </Container>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
				
			|||||||
@ -27,6 +27,8 @@ export default function Integration({ config, params }) {
 | 
				
			|||||||
        title,
 | 
					        title,
 | 
				
			||||||
        date: DateTime.fromISO(event.releaseDate),
 | 
					        date: DateTime.fromISO(event.releaseDate),
 | 
				
			||||||
        color: config?.color ?? "green",
 | 
					        color: config?.color ?? "green",
 | 
				
			||||||
 | 
					        isCompleted: event.grabbed,
 | 
				
			||||||
 | 
					        additional: "",
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -29,16 +29,22 @@ export default function Integration({ config, params }) {
 | 
				
			|||||||
        title: cinemaTitle,
 | 
					        title: cinemaTitle,
 | 
				
			||||||
        date: DateTime.fromISO(event.inCinemas),
 | 
					        date: DateTime.fromISO(event.inCinemas),
 | 
				
			||||||
        color: config?.color ?? "amber",
 | 
					        color: config?.color ?? "amber",
 | 
				
			||||||
 | 
					        isCompleted: event.isAvailable,
 | 
				
			||||||
 | 
					        additional: "",
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      eventsToAdd[physicalTitle] = {
 | 
					      eventsToAdd[physicalTitle] = {
 | 
				
			||||||
        title: physicalTitle,
 | 
					        title: physicalTitle,
 | 
				
			||||||
        date: DateTime.fromISO(event.physicalRelease),
 | 
					        date: DateTime.fromISO(event.physicalRelease),
 | 
				
			||||||
        color: config?.color ?? "cyan",
 | 
					        color: config?.color ?? "cyan",
 | 
				
			||||||
 | 
					        isCompleted: event.isAvailable,
 | 
				
			||||||
 | 
					        additional: "",
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      eventsToAdd[digitalTitle] = {
 | 
					      eventsToAdd[digitalTitle] = {
 | 
				
			||||||
        title: digitalTitle,
 | 
					        title: digitalTitle,
 | 
				
			||||||
        date: DateTime.fromISO(event.digitalRelease),
 | 
					        date: DateTime.fromISO(event.digitalRelease),
 | 
				
			||||||
        color: config?.color ?? "emerald",
 | 
					        color: config?.color ?? "emerald",
 | 
				
			||||||
 | 
					        isCompleted: event.isAvailable,
 | 
				
			||||||
 | 
					        additional: "",
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,8 @@ export default function Integration({ config, params }) {
 | 
				
			|||||||
        title,
 | 
					        title,
 | 
				
			||||||
        date: DateTime.fromISO(event.releaseDate),
 | 
					        date: DateTime.fromISO(event.releaseDate),
 | 
				
			||||||
        color: config?.color ?? "rose",
 | 
					        color: config?.color ?? "rose",
 | 
				
			||||||
 | 
					        isCompleted: event.grabbed,
 | 
				
			||||||
 | 
					        additional: "",
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -26,9 +26,11 @@ export default function Integration({ config, params }) {
 | 
				
			|||||||
      const title = `${event.series.title ?? event.title} - S${event.seasonNumber}E${event.episodeNumber}`;
 | 
					      const title = `${event.series.title ?? event.title} - S${event.seasonNumber}E${event.episodeNumber}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      eventsToAdd[title] = {
 | 
					      eventsToAdd[title] = {
 | 
				
			||||||
        title,
 | 
					        title: `${event.series.title ?? event.title}`,
 | 
				
			||||||
        date: DateTime.fromISO(event.airDateUtc),
 | 
					        date: DateTime.fromISO(event.airDateUtc),
 | 
				
			||||||
        color: config?.color ?? "teal",
 | 
					        color: config?.color ?? "teal",
 | 
				
			||||||
 | 
					        isCompleted: event.hasFile,
 | 
				
			||||||
 | 
					        additional: `S${event.seasonNumber} E${event.episodeNumber}`,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,43 +1,16 @@
 | 
				
			|||||||
import { useContext, useEffect, useMemo } from "react";
 | 
					import { useContext, useMemo } from "react";
 | 
				
			||||||
import { DateTime, Info } from "luxon";
 | 
					import { DateTime, Info } from "luxon";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import { useTranslation } from "next-i18next";
 | 
					import { useTranslation } from "next-i18next";
 | 
				
			||||||
 | 
					import { IoMdCheckmarkCircleOutline } from "react-icons/io";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { EventContext, ShowDateContext } from "../../utils/contexts/calendar";
 | 
					import { EventContext } from "../../utils/contexts/calendar";
 | 
				
			||||||
 | 
					 | 
				
			||||||
const colorVariants = {
 | 
					 | 
				
			||||||
  // https://tailwindcss.com/docs/content-configuration#dynamic-class-names
 | 
					 | 
				
			||||||
  amber: "bg-amber-500",
 | 
					 | 
				
			||||||
  blue: "bg-blue-500",
 | 
					 | 
				
			||||||
  cyan: "bg-cyan-500",
 | 
					 | 
				
			||||||
  emerald: "bg-emerald-500",
 | 
					 | 
				
			||||||
  fuchsia: "bg-fuchsia-500",
 | 
					 | 
				
			||||||
  gray: "bg-gray-500",
 | 
					 | 
				
			||||||
  green: "bg-green-500",
 | 
					 | 
				
			||||||
  indigo: "bg-indigo-500",
 | 
					 | 
				
			||||||
  lime: "bg-lime-500",
 | 
					 | 
				
			||||||
  neutral: "bg-neutral-500",
 | 
					 | 
				
			||||||
  orange: "bg-orange-500",
 | 
					 | 
				
			||||||
  pink: "bg-pink-500",
 | 
					 | 
				
			||||||
  purple: "bg-purple-500",
 | 
					 | 
				
			||||||
  red: "bg-red-500",
 | 
					 | 
				
			||||||
  rose: "bg-rose-500",
 | 
					 | 
				
			||||||
  sky: "bg-sky-500",
 | 
					 | 
				
			||||||
  slate: "bg-slate-500",
 | 
					 | 
				
			||||||
  stone: "bg-stone-500",
 | 
					 | 
				
			||||||
  teal: "bg-teal-500",
 | 
					 | 
				
			||||||
  violet: "bg-violet-500",
 | 
					 | 
				
			||||||
  white: "bg-white-500",
 | 
					 | 
				
			||||||
  yellow: "bg-yellow-500",
 | 
					 | 
				
			||||||
  zinc: "bg-zinc-500",
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cellStyle = "relative w-10 flex items-center justify-center flex-col";
 | 
					const cellStyle = "relative w-10 flex items-center justify-center flex-col";
 | 
				
			||||||
const monthButton = "pl-6 pr-6 ml-2 mr-2 hover:bg-theme-100/20 dark:hover:bg-white/5 rounded-md cursor-pointer";
 | 
					const monthButton = "pl-6 pr-6 ml-2 mr-2 hover:bg-theme-100/20 dark:hover:bg-white/5 rounded-md cursor-pointer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function Day({ weekNumber, weekday, events }) {
 | 
					export function Day({ weekNumber, weekday, events, colorVariants, showDate, setShowDate }) {
 | 
				
			||||||
  const currentDate = DateTime.now();
 | 
					  const currentDate = DateTime.now();
 | 
				
			||||||
  const { showDate, setShowDate } = useContext(ShowDateContext);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const cellDate = showDate.set({ weekday, weekNumber }).startOf("day");
 | 
					  const cellDate = showDate.set({ weekday, weekNumber }).startOf("day");
 | 
				
			||||||
  const filteredEvents = events?.filter(
 | 
					  const filteredEvents = events?.filter(
 | 
				
			||||||
@ -105,7 +78,13 @@ export function Event({ event }) {
 | 
				
			|||||||
    >
 | 
					    >
 | 
				
			||||||
      <span className="absolute left-2 text-left text-xs mt-[2px] truncate text-ellipsis" style={{ width: "96%" }}>
 | 
					      <span className="absolute left-2 text-left text-xs mt-[2px] truncate text-ellipsis" style={{ width: "96%" }}>
 | 
				
			||||||
        {event.title}
 | 
					        {event.title}
 | 
				
			||||||
 | 
					        {event.additional ? ` - ${event.additional}` : ""}
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
 | 
					      {event.isCompleted && (
 | 
				
			||||||
 | 
					        <span className="text-right text-xs flex justify-end mr-1 mt-1 z-10 ">
 | 
				
			||||||
 | 
					          <IoMdCheckmarkCircleOutline />
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -120,19 +99,12 @@ const dayInWeekId = {
 | 
				
			|||||||
  sunday: 7,
 | 
					  sunday: 7,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function MonthlyView({ service }) {
 | 
					export default function Monthly({ service, colorVariants, showDate, setShowDate }) {
 | 
				
			||||||
  const { widget } = service;
 | 
					  const { widget } = service;
 | 
				
			||||||
  const { i18n } = useTranslation();
 | 
					  const { i18n } = useTranslation();
 | 
				
			||||||
  const { showDate, setShowDate } = useContext(ShowDateContext);
 | 
					 | 
				
			||||||
  const { events } = useContext(EventContext);
 | 
					  const { events } = useContext(EventContext);
 | 
				
			||||||
  const currentDate = DateTime.now().setLocale(i18n.language).startOf("day");
 | 
					  const currentDate = DateTime.now().setLocale(i18n.language).startOf("day");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    if (!showDate) {
 | 
					 | 
				
			||||||
      setShowDate(currentDate);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const dayNames = Info.weekdays("short", { locale: i18n.language });
 | 
					  const dayNames = Info.weekdays("short", { locale: i18n.language });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const firstDayInWeekCalendar = widget?.firstDayInWeek ? widget?.firstDayInWeek?.toLowerCase() : "monday";
 | 
					  const firstDayInWeekCalendar = widget?.firstDayInWeek ? widget?.firstDayInWeek?.toLowerCase() : "monday";
 | 
				
			||||||
@ -211,6 +183,9 @@ export default function MonthlyView({ service }) {
 | 
				
			|||||||
                weekNumber={weekNumber}
 | 
					                weekNumber={weekNumber}
 | 
				
			||||||
                weekday={dayInWeek}
 | 
					                weekday={dayInWeek}
 | 
				
			||||||
                events={eventsArray}
 | 
					                events={eventsArray}
 | 
				
			||||||
 | 
					                colorVariants={colorVariants}
 | 
				
			||||||
 | 
					                showDate={showDate}
 | 
				
			||||||
 | 
					                setShowDate={setShowDate}
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
@ -219,8 +194,9 @@ export default function MonthlyView({ service }) {
 | 
				
			|||||||
        <div className="flex flex-col pt-1 pb-1">
 | 
					        <div className="flex flex-col pt-1 pb-1">
 | 
				
			||||||
          {eventsArray
 | 
					          {eventsArray
 | 
				
			||||||
            ?.filter((event) => showDate.startOf("day").toUnixInteger() === event.date?.startOf("day").toUnixInteger())
 | 
					            ?.filter((event) => showDate.startOf("day").toUnixInteger() === event.date?.startOf("day").toUnixInteger())
 | 
				
			||||||
 | 
					            .slice(0, widget?.maxEvents ?? 10)
 | 
				
			||||||
            .map((event) => (
 | 
					            .map((event) => (
 | 
				
			||||||
              <Event key={`event${event.title}`} event={event} />
 | 
					              <Event key={`event${event.title}-${event.additional}`} event={event} />
 | 
				
			||||||
            ))}
 | 
					            ))}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user