diff --git a/.cspell.json b/.cspell.json
index 56963e340..91488e9ca 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -21,6 +21,7 @@
"APPSTORE",
"arrowleft",
"asel",
+ "alldays",
"Authentificate",
"authjs",
"barcodes",
@@ -74,6 +75,7 @@
"Darkmode",
"datas",
"dataToDisplay",
+ "daygrid",
"dearmor",
"deepscan",
"Defaul",
@@ -117,6 +119,7 @@
"Filder",
"filtmembers",
"firstname",
+ "fullcalendar",
"flaticon",
"fomated",
"Formated",
@@ -325,6 +328,7 @@
"Transpiles",
"tsbuildinfo",
"typeof",
+ "timegrid",
"uicolors",
"uidotdev",
"UIUX",
diff --git a/apps/web/app/[locale]/calendar/page.tsx b/apps/web/app/[locale]/calendar/page.tsx
new file mode 100644
index 000000000..bb0bd78e5
--- /dev/null
+++ b/apps/web/app/[locale]/calendar/page.tsx
@@ -0,0 +1,84 @@
+"use client"
+import { useOrganizationTeams } from '@app/hooks';
+import { fullWidthState } from '@app/stores/fullWidth';
+import { clsxm } from '@app/utils';
+import HeaderTabs from '@components/pages/main/header-tabs';
+import { PeoplesIcon } from 'assets/svg';
+import { withAuthentication } from 'lib/app/authenticator';
+import { Breadcrumb, Button, Container, Divider } from 'lib/components';
+import { SetupFullCalendar } from 'lib/features'
+import { Footer, MainLayout } from 'lib/layout';
+import { useTranslations } from 'next-intl';
+import { useParams } from 'next/navigation';
+import React, { useMemo } from 'react'
+import { useRecoilValue } from 'recoil';
+
+const CalendarPage = () => {
+ const t = useTranslations();
+ const fullWidth = useRecoilValue(fullWidthState);
+ const { activeTeam, isTrackingEnabled } = useOrganizationTeams();
+ const params = useParams<{ locale: string }>();
+ const currentLocale = params ? params.locale : null;
+ const breadcrumbPath = useMemo(
+ () => [
+ { title: JSON.parse(t('pages.home.BREADCRUMB')), href: '/' },
+ { title: activeTeam?.name || '', href: '/' },
+ { title: "CALENDAR", href: `/${currentLocale}/calendar` }
+ ],
+ [activeTeam?.name, currentLocale, t]
+ );
+ return (
+ <>
+
+
+
+
+
+
+
+ CALENDAR
+
+
+
+
+
+
+ {/* */}
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default withAuthentication(CalendarPage, { displayName: 'Calender' });
diff --git a/apps/web/app/[locale]/kanban/page.tsx b/apps/web/app/[locale]/kanban/page.tsx
index 646b39716..490d1b61f 100644
--- a/apps/web/app/[locale]/kanban/page.tsx
+++ b/apps/web/app/[locale]/kanban/page.tsx
@@ -166,11 +166,10 @@ const Kanban = () => {
setActiveTab(tab.value)}
- className={`cursor-pointer pt-2.5 px-5 pb-[30px] text-base font-semibold ${
- activeTab === tab.value
+ className={`cursor-pointer pt-2.5 px-5 pb-[30px] text-base font-semibold ${activeTab === tab.value
? 'border-b-[#3826A6] text-[#3826A6] dark:text-white dark:border-b-white'
: 'border-b-white dark:border-b-[#191A20] dark:text-white text-[#282048]'
- }`}
+ }`}
style={{
borderBottomWidth: '3px',
borderBottomStyle: 'solid'
diff --git a/apps/web/app/api/livekit/route.ts b/apps/web/app/api/livekit/route.ts
index bd485b8fd..8e07b29b9 100644
--- a/apps/web/app/api/livekit/route.ts
+++ b/apps/web/app/api/livekit/route.ts
@@ -32,8 +32,21 @@ export async function GET(req: NextRequest) {
}
try {
- const at = new AccessToken(apiKey, apiSecret, { identity: username });
- at.addGrant({ room, roomJoin: true, canPublish: true, canSubscribe: true, roomRecord: true });
+ const at = new AccessToken(apiKey, apiSecret, { identity: username, ttl: '1h' });
+ at.addGrant({
+ room,
+ roomJoin: true,
+ canPublish: true,
+ canSubscribe: true,
+ roomRecord: true,
+ roomCreate: true,
+ roomAdmin: true,
+ recorder: true,
+ roomList: true,
+ canUpdateOwnMetadata: true,
+ agent: true,
+ canPublishData: true,
+ });
const token = await at.toJwt();
return NextResponse.json({ token: token });
} catch (error) {
diff --git a/apps/web/lib/features/index.ts b/apps/web/lib/features/index.ts
index 688b0f985..81fec7bd4 100644
--- a/apps/web/lib/features/index.ts
+++ b/apps/web/lib/features/index.ts
@@ -36,3 +36,6 @@ export * from './user-profile-tasks';
export * from './languages/language-item';
export * from './timezones/timezone-item';
export * from './position/position-item';
+
+
+export * from './integrations/calendar/setup-full-calendar'
diff --git a/apps/web/lib/features/integrations/calendar/setup-full-calendar.tsx b/apps/web/lib/features/integrations/calendar/setup-full-calendar.tsx
new file mode 100644
index 000000000..ffa421a35
--- /dev/null
+++ b/apps/web/lib/features/integrations/calendar/setup-full-calendar.tsx
@@ -0,0 +1,291 @@
+"use client"
+import React, { useState, useRef } from 'react';
+import { LuCalendarPlus } from "react-icons/lu";
+import { IoIosArrowDown, IoIosArrowForward } from "react-icons/io";
+import { IoTimeSharp } from "react-icons/io5";
+import { MdTimer } from "react-icons/md";
+import FullCalendar from '@fullcalendar/react';
+import interactionPlugin from '@fullcalendar/interaction';
+import dayGridPlugin from '@fullcalendar/daygrid';
+import timeGridPlugin from '@fullcalendar/timegrid';
+import listPlugin from '@fullcalendar/list';
+import { startOfYear, endOfYear, format } from 'date-fns';
+import Image from 'next/image';
+import { Button } from 'lib/components';
+import { SettingFilterIcon } from 'assets/svg';
+import { YearDateFilter } from './year-picker-filter';
+import { cn } from 'lib/utils';
+// import { IOrganizationTeamList } from '@app/interfaces';
+
+interface Event {
+ id?: string;
+ title: string;
+ start: string;
+ times?: string,
+ color: string;
+ textColor?: string,
+ padding?: number,
+ extendedProps?: {
+ icon?: JSX.Element;
+ },
+
+}
+
+export function SetupFullCalendar() {
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
+ // const [newEventTitle, setNewEventTitle] = useState('');
+ const calendarRef = useRef
(null);
+ const [selectedDate, setSelectedDate] = useState('');
+ const [events, setEvents] = useState([
+ {
+ id: '10',
+ title: 'Auto',
+ start: '2024-08-01',
+ color: '#dcfce7',
+ textColor: "#16a34a",
+ extendedProps: {
+ icon: ,
+ },
+
+
+ },
+ {
+ id: '13',
+ title: 'Manual',
+ start: '2024-08-01',
+ color: '#ffedd5',
+ textColor: "#f97316",
+ extendedProps: {
+ icon: ,
+ },
+ },
+ {
+ id: '12',
+ title: 'Auto',
+ start: '2024-08-01',
+ color: '#dcfce7',
+ textColor: "#16a34a",
+ extendedProps: {
+ icon: ,
+ },
+
+ },
+ {
+ id: '11',
+ title: 'Manual',
+ start: '2024-08-02',
+ color: '#ffedd5',
+ textColor: "#f97316",
+ extendedProps: {
+ icon: ,
+ },
+ },
+ ]);
+
+ const handleDateClick = (info: { dateStr: string }) => {
+ setSelectedDate(info?.dateStr);
+ setIsDialogOpen((prev) => !prev);
+ };
+
+ const renderEventContent = (eventInfo: any) => {
+ return (
+
+
+ {eventInfo.event.extendedProps.icon}
+ {eventInfo.event.title}
+
+
05:30h
+
+ );
+ };
+
+ const dayCellClassNames = (arg: any) => {
+ const today = format(new Date(), 'yyyy-MM-dd');
+ const dateStr = format(arg.date, 'yyyy-MM-dd');
+ if (today === dateStr) {
+ return ['today-cell'];
+ }
+ return ['alldays-cell'];
+ };
+
+ const handleEventClick = (info: { event: { id: string; startStr: string } }) => {
+ const isDelete = confirm(`Do you want to delete the event: ${info.event?.id}?`);
+ if (isDelete) {
+ const updatedEvents = events.filter(event =>
+ event.id !== info.event.id || event.start !== info.event.startStr
+ );
+ setEvents(updatedEvents);
+ }
+ };
+
+ const handleEventDrop = (info: { event: { id: string; startStr: string } }) => {
+ const updatedEvents = events.map(event =>
+ event.id === info.event.id ? { ...event, start: info.event.startStr } : event
+ );
+ setEvents(updatedEvents);
+ };
+
+
+
+
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
{
+ const start = startOfYear(currentDate);
+ const end = endOfYear(currentDate);
+ return { start, end };
+ },
+ titleFormat: { year: 'numeric' },
+ eventClassNames: (info) => info.event.classNames,
+ },
+ }}
+ // weekends={false}
+ dayCellClassNames={dayCellClassNames}
+ initialView="dayGridMonth"
+ events={events}
+ dateClick={handleDateClick}
+ eventClick={handleEventClick}
+ eventDrop={handleEventDrop}
+ eventContent={renderEventContent}
+ editable={true}
+
+ />
+
+
+ {isDialogOpen && (
+
+
+
+ )}
+
+ )
+}
+
+
+
+export const CardItems = ({ selectedDate }: { selectedDate: Date }) => {
+ return (
+
+
+ {format(selectedDate, 'PPP')}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+
+export const CardItemsMember = ({ imageUrl, name, time }: { imageUrl?: string, name?: string, time?: string }) => {
+ return (
+
+
+
+
+ {name}
+ {time}
+
+
+
+
+ )
+}
+
+export const CardItemsProjects = ({ logo, title, totalHours }: { logo?: string, title?: string, totalHours?: string }) => {
+ return (
+
+
+
+
+ {title}
+ {totalHours}
+
+
+
+
+ )
+}
+
+
+export function TotalHours() {
+ return (
+
+
+
+ Total Hours 240
+
+
+
+ )
+}
diff --git a/apps/web/lib/features/integrations/calendar/year-picker-filter.tsx b/apps/web/lib/features/integrations/calendar/year-picker-filter.tsx
new file mode 100644
index 000000000..ef59f2fd6
--- /dev/null
+++ b/apps/web/lib/features/integrations/calendar/year-picker-filter.tsx
@@ -0,0 +1,59 @@
+"use client"
+import * as React from "react"
+import { CalendarDaysIcon as CalendarIcon } from "lucide-react"
+import { MdKeyboardArrowLeft, MdKeyboardArrowRight } from "react-icons/md";
+import FullCalendar from "@fullcalendar/react";
+import moment from "moment";
+interface IYearDateFilter {
+ calendarRef: React.MutableRefObject
+}
+export function YearDateFilter({ calendarRef }: IYearDateFilter) {
+ const current = calendarRef.current;
+ const [currentDate, setCurrentDate] = React.useState(new Date());
+
+
+ const updateCurrentDate = () => {
+ if (calendarRef.current) {
+ const calendarApi = calendarRef.current.getApi();
+ setCurrentDate(calendarApi.getDate());
+
+ }
+ };
+
+ function goNext() {
+ if (current) {
+ const calendarApi = current.getApi()
+ calendarApi.next()
+ updateCurrentDate();
+ }
+ }
+ function goPrev() {
+ if (current) {
+ const calendarApi = current.getApi()
+ calendarApi.prev();
+ updateCurrentDate();
+ }
+ }
+
+ React.useEffect(() => {
+ updateCurrentDate();
+ }, [updateCurrentDate]); // deepscan-disable-line
+
+ return (
+
+
+
+ {moment(currentDate).format('MMM')}{" "}{currentDate.getFullYear()}
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/web/package.json b/apps/web/package.json
index 45189cfb7..3607108c5 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -29,6 +29,12 @@
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@excalidraw/excalidraw": "^0.15.3",
+ "@fullcalendar/core": "^6.1.15",
+ "@fullcalendar/daygrid": "^6.1.15",
+ "@fullcalendar/interaction": "^6.1.15",
+ "@fullcalendar/list": "^6.1.15",
+ "@fullcalendar/react": "^6.1.15",
+ "@fullcalendar/timegrid": "^6.1.15",
"@headlessui/react": "^1.7.7",
"@heroicons/react": "^2.0.12",
"@jitsi/react-sdk": "^1.3.0",
diff --git a/yarn.lock b/yarn.lock
index 5c2486c87..a0981a975 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2616,6 +2616,40 @@
dependencies:
tslib "^2.4.0"
+"@fullcalendar/core@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.yarnpkg.com/@fullcalendar/core/-/core-6.1.15.tgz#6c3f5259fc4589870228853072131219bb533f6e"
+ integrity sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==
+ dependencies:
+ preact "~10.12.1"
+
+"@fullcalendar/daygrid@^6.1.15", "@fullcalendar/daygrid@~6.1.15":
+ version "6.1.15"
+ resolved "https://registry.yarnpkg.com/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz#91208b0955ba805ddad285a53ee6f53855146963"
+ integrity sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==
+
+"@fullcalendar/interaction@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.yarnpkg.com/@fullcalendar/interaction/-/interaction-6.1.15.tgz#1c685d5c269388d4877b75ab2185e97d7c386cc7"
+ integrity sha512-DOTSkofizM7QItjgu7W68TvKKvN9PSEEvDJceyMbQDvlXHa7pm/WAVtAc6xSDZ9xmB1QramYoWGLHkCYbTW1rQ==
+
+"@fullcalendar/list@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.yarnpkg.com/@fullcalendar/list/-/list-6.1.15.tgz#d9b7ff0a50d7efa0d31234ed6caea06db6090c29"
+ integrity sha512-U1bce04tYDwkFnuVImJSy2XalYIIQr6YusOWRPM/5ivHcJh67Gm8CIMSWpi3KdRSNKFkqBxLPkfZGBMaOcJYug==
+
+"@fullcalendar/react@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.yarnpkg.com/@fullcalendar/react/-/react-6.1.15.tgz#3198b4a64e256afd37c9760c8741a9af89ade894"
+ integrity sha512-L0b9hybS2J4e7lq6G2CD4nqriyLEqOH1tE8iI6JQjAMTVh5JicOo5Mqw+fhU5bJ7hLfMw2K3fksxX3Ul1ssw5w==
+
+"@fullcalendar/timegrid@^6.1.15":
+ version "6.1.15"
+ resolved "https://registry.yarnpkg.com/@fullcalendar/timegrid/-/timegrid-6.1.15.tgz#c4630b7c03c813065154c6e3981f8d51d9d692e5"
+ integrity sha512-61ORr3A148RtxQ2FNG7JKvacyA/TEVZ7z6I+3E9Oeu3dqTf6M928bFcpehRTIK6zIA6Yifs7BeWHgOE9dFnpbw==
+ dependencies:
+ "@fullcalendar/daygrid" "~6.1.15"
+
"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
@@ -21693,6 +21727,11 @@ preact@10.11.3:
resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19"
integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==
+preact@~10.12.1:
+ version "10.12.1"
+ resolved "https://registry.yarnpkg.com/preact/-/preact-10.12.1.tgz#8f9cb5442f560e532729b7d23d42fd1161354a21"
+ integrity sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==
+
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"