Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

칸반보드를 캘린더에 연동 #29

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
71260f7
chore: react-calendar, moment 라이브러리 설치
Jayjunyoung Jan 22, 2024
e06fb5b
feat: calendar 버튼 추가, Calendar css 커스터마이징 예정
Jayjunyoung Jan 22, 2024
aa946fb
feat: 초기 calenadr 컴포넌트
Jayjunyoung Jan 22, 2024
07d1956
feat: calendar 추가->sidebar에 추가 구현
Jayjunyoung Jan 23, 2024
84bf1f3
chore: calendarItem 패딩 적용
Jayjunyoung Jan 23, 2024
e0a425f
chore: padding 다시 설정
Jayjunyoung Jan 23, 2024
8667f84
chore: calendar icon 설정
Jayjunyoung Jan 23, 2024
0c1715a
design: calendar 정가운데에 배치
Jayjunyoung Jan 24, 2024
891c0da
design: calendar파일에 layout으로 가운데 배치
Jayjunyoung Jan 24, 2024
b1cd744
feat: calendar 템플릿 구현, css 추후 보완 예정
Jayjunyoung Jan 24, 2024
cccc8b8
feat: calendar 기본 로직 구현
Jayjunyoung Jan 26, 2024
e12ad24
Merge branch 'main' of https://github.com/Google-Developer-Student-Cl…
Jayjunyoung Jan 26, 2024
abaa27c
chore: 색깔 및 flex 구조 변경
Jayjunyoung Jan 29, 2024
ab9b70a
feat: Calendar list, item 구현
Jayjunyoung Jan 29, 2024
7bc9c49
feat: Mouse 커서 감지되면 Plus 아이콘 렌더링
Jayjunyoung Jan 30, 2024
193a949
feat: Mouse 커서 감지된 해당 날짜에만 Plus 아이콘 렌더링
Jayjunyoung Jan 30, 2024
045cf32
refactor: handleMouseLeave 매개변수 제거
Jayjunyoung Jan 30, 2024
173560a
feat: Calendar 생성시 calendarId로 Redirect 구현
Jayjunyoung Feb 5, 2024
d88530f
feat: Calendar db에 content필드 데이터 입력 구현
Jayjunyoung Feb 12, 2024
20ea77a
feat: main브렌치와 병합 및 초기 데이터가 공백일때는 출력 안되게 구현
Jayjunyoung Feb 12, 2024
b7cc699
refactor: onNewElement 반환값 변경
Jayjunyoung Feb 12, 2024
0544c4d
feat: 컴포넌트 분리 및 indexing 작업 중
Jayjunyoung Feb 13, 2024
aa831bc
refactor: props key->index로 변경
Jayjunyoung Feb 13, 2024
a95c2dc
feat: convex server에 index 요소 추가 및 인덱스 매칭 완료
Jayjunyoung Feb 13, 2024
66850b4
remove: i 제거
Jayjunyoung Feb 13, 2024
f1472b7
feat: 문서는 최대2개 까지로 제한
Jayjunyoung Feb 14, 2024
b7bc752
CalendarDay 컴포넌트로 editable props 전송
Jayjunyoung Feb 14, 2024
1cf7d1f
feat: 문서 3개부터는 scroll 되도록 구현
Jayjunyoung Feb 14, 2024
90f13c1
feat: calendar 문서 name 수정 구현
Jayjunyoung Feb 15, 2024
437cbc8
feat: Calendar 문서 삭제 기능 구현
Jayjunyoung Feb 16, 2024
5cb9b55
refactor: Minus -> Trash2 아이콘 변경
Jayjunyoung Feb 16, 2024
5bc8a5d
feat: See 링크가 연결된 Calendar 참조
hyuckkim Feb 19, 2024
bfcdc65
feat: 캘린더 문서 등록 시 달에 따른 문서등록 구현 완료
Jayjunyoung Feb 29, 2024
e1acf99
Merge remote-tracking branch 'base/junyoung' into kanbanboard-connect…
hyuckkim Mar 1, 2024
82e936b
Merge branch 'feature-kanbanboard' into kanbanboard-connect-calendar
hyuckkim Mar 1, 2024
145c975
Merge branch 'fix-import' into kanbanboard-connect-calendar
hyuckkim Mar 1, 2024
b14ae9c
feat: index 날짜로 바꾸는 함수 추가
hyuckkim Mar 1, 2024
29b650d
fix: ArrayDragSpace에 반복문 추가
hyuckkim Mar 1, 2024
a147745
feat: boardDocument까지 Date 전달 (아직 실 기능은 없음)
hyuckkim Mar 1, 2024
fd9b3c1
Merge remote-tracking branch 'base/main' into kanbanboard-connect-cal…
hyuckkim Mar 1, 2024
fd4a114
refactor: 엔터키 Event에 따른 문서 등록 구현
Jayjunyoung Mar 2, 2024
128e554
Merge remote-tracking branch 'base/junyoung' into kanbanboard-connect…
hyuckkim Mar 2, 2024
aee325e
Merge remote-tracking branch 'base/main' into kanbanboard-connect-cal…
hyuckkim Mar 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions app/(main)/(routes)/boards/[boardId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { BoardToolbar } from "@/components/KanbanBoard/board-toolbar";
import BoardView from "@/components/KanbanBoard/board-view";
import { Skeleton } from "@/components/ui/skeleton";
import { api } from "@/convex/_generated/api";
import { Id } from "@/convex/_generated/dataModel";
import { useMutation, useQuery } from "convex/react";
import { Doc, Id } from "@/convex/_generated/dataModel";
import { useConvex, useMutation, useQuery } from "convex/react";
import { useEffect, useState } from "react";

interface BoardIdPageProps {
params: {
Expand All @@ -17,7 +18,33 @@ const BoardIdPage = ({ params }: BoardIdPageProps) => {
const board = useQuery(api.boards.getById, {
boardId: params.boardId
});
const convex = useConvex();

const [calendar, setCalendar] = useState<Doc<"calendars"> | undefined>(undefined);

const update = useMutation(api.boards.update);
const removeCalendar = useMutation(api.boards.unconnectCalendar);

useEffect(() => {
const onUnconnectCalendar = () => {
removeCalendar({
id: params.boardId,
})
}
console.log("???");

if (!(board?.connectedCalendar)) setCalendar(undefined);
else {
convex.query(api.calendars.getById, {
calendarId: board.connectedCalendar
}).then(v => {
setCalendar(v);
}).catch(_ => {
onUnconnectCalendar();
});
}
}, [board?.connectedCalendar, convex, params.boardId, removeCalendar]);


if (board === undefined) {
return (
Expand Down Expand Up @@ -45,11 +72,15 @@ const BoardIdPage = ({ params }: BoardIdPageProps) => {

return (
<div>
<BoardToolbar initialData={board}/>
<BoardToolbar
initialData={board}
calendar={calendar}
/>
<BoardView
onChange={onUpdate}
initialContent={board.content}
editable={true}
connectedCalendar={calendar}
/>
</div>
)
Expand Down
8 changes: 4 additions & 4 deletions app/(main)/_components/KanbanBoard/board-misc-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useMutation, useQuery } from "convex/react";
import { toast } from "sonner";
import { Calendar, Eye, MoreHorizontal, Trash, Unlink } from "lucide-react";

import { Id } from "@/convex/_generated/dataModel";
import { Doc, Id } from "@/convex/_generated/dataModel";
import {
DropdownMenu,
DropdownMenuTrigger,
Expand All @@ -25,14 +25,14 @@ import Link from "next/link";

interface MenuProps {
documentId: Id<"boards">;
calendar?: Doc<"calendars">;
}

export const BoardMiscMenu = ({ documentId }: MenuProps) => {
export const BoardMiscMenu = ({ documentId, calendar }: MenuProps) => {
const router = useRouter();
const { user } = useUser();

const board = useQuery(api.boards.getById, { boardId: documentId });
const calendar = useQuery(api.calendars.getById, (!!(board?.connectedCalendar)) ? { newCalendar: board?.connectedCalendar } : "skip");

const calendars = useQuery(api.calendars.getSidebar);
const connectCalendar = useMutation(api.boards.connectCalendar);
Expand Down Expand Up @@ -74,7 +74,7 @@ export const BoardMiscMenu = ({ documentId }: MenuProps) => {
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuItem asChild>
<Link href={'/calendars'}>
<Link href={`/calendars/${calendar._id}`}>
<Eye className="h-4 w-4 mr-2" />
See
</Link>
Expand Down
7 changes: 5 additions & 2 deletions components/KanbanBoard/board-document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { KanbanBoardDocument } from "@/components/KanbanBoard/kanbanboard.types"
import { BoardDocumentTitle } from "@/components/KanbanBoard/board-document-title";
import { toast } from "sonner";
import { BoardColorPicker } from "@/app/(main)/_components/KanbanBoard/board-color-picker";
import { formatDistance } from "date-fns";

export const BoardDocument = ({
boardDocument: {
Expand All @@ -27,12 +28,14 @@ export const BoardDocument = ({
onDocumentSetAttribute,
},
onDragChange,
date,
}: {
boardDocument: KanbanBoardDocument,
document: Doc<"documents">,
editable?: boolean,
editor: KanbanBoardProps,
onDragChange?: (status: ("before" | "after" | "none")) => void,
date?: Date,
}) => {
const { resolvedTheme } = useTheme();
const rootRef = useRef<ElementRef<"div">>(null);
Expand Down Expand Up @@ -206,11 +209,11 @@ export const BoardDocument = ({
</div>
</div>
)}
{false && (
{date && (
<div className="flex">
<AlarmClockCheck className="w-4 h-4 mr-2" />
<div className="text-nowrap overflow-hidden text-ellipsis max-w-20">
{"in 1234 years"}
{formatDistance(new Date(), date)}
</div>
</div>
)}
Expand Down
3 changes: 3 additions & 0 deletions components/KanbanBoard/board-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ export const BoardElement = ({
editable,
documents,
onDragChange,
calendarData,
}: {
editor: KanbanBoardProps,
element: KanbanBoardElement,
editable?: boolean,
documents?: Doc<"documents">[] | undefined,
onDragChange?: (status: ("before" | "after" | "none")) => void,
calendarData?: {[document: Id<"documents">]: Date},
}) => {
const { resolvedTheme } = useTheme();
const rootRef = useRef<ElementRef<"div">>(null);
Expand Down Expand Up @@ -284,6 +286,7 @@ export const BoardElement = ({
editable={editable}
editor={editor}
onDragChange={(status) => onDocumentDragOver(status, i)}
date={calendarData?.[document.board._id]}
/>
))}
</ArrayDragSpace>
Expand Down
5 changes: 3 additions & 2 deletions components/KanbanBoard/board-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import { BoardMiscMenu } from "@/app/(main)/_components/KanbanBoard/board-misc-m
interface BoardToolbarProps {
initialData: Doc<"boards">;
preview?: boolean;
calendar?: Doc<"calendars">;
}

export const BoardToolbar = ({ initialData, preview }: BoardToolbarProps) => {
export const BoardToolbar = ({ initialData, preview, calendar }: BoardToolbarProps) => {
const inputRef = useRef<ElementRef<"textarea">>(null);
const [isEditing, setIsEditing] = useState(false);
const [value, setValue] = useState(initialData.title);
Expand Down Expand Up @@ -129,7 +130,7 @@ export const BoardToolbar = ({ initialData, preview }: BoardToolbarProps) => {
{!preview && (
<div className="flex gap-x-2 m-2">
<PublishBoard initialData={initialData} />
<BoardMiscMenu documentId={initialData._id} />
<BoardMiscMenu documentId={initialData._id} calendar={calendar}/>
</div>
)}
</div>
Expand Down
27 changes: 25 additions & 2 deletions components/KanbanBoard/board-view.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
"use client";

import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { useKanbanBoard } from "@/hooks/use-kanban-board";
import { PlusCircle } from "lucide-react";

import { BoardElement } from "./board-element";
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
import { ArrayDragSpace } from "../array-drag-space";
import { cn } from "@/lib/utils";
import { Doc, Id } from "@/convex/_generated/dataModel";
import { Calendar, parseDateByIndex } from "@/types/calendar";

const BoardView = ({
onChange,
initialContent,
editable,
connectedCalendar,
}: {
onChange?: (value: string) => void,
initialContent?: string,
editable?: boolean
connectedCalendar?: Doc<"calendars">,
editable?: boolean,
}) => {
const documents = useQuery(api.documents.getSearch, {});
const [dragSelected, setDragSelected] = useState<number | undefined>(undefined);
Expand All @@ -28,6 +33,23 @@ const BoardView = ({
}
});

const [orientedData, setOrientedData] = useState<{[document: Id<"documents">]: Date}>({});
useEffect(() => {
const newData: {[document: Id<"documents">]: Date} = {};
if (connectedCalendar?.content) {
const calendarData: Calendar = JSON.parse(connectedCalendar.content);
calendarData.forEach(e => {
const parsedDate = parseDateByIndex(e.calendarMonth, e.calendarIndex);
e.content.forEach(d => {
if (false) { // Todo: calendarDocument가 문서를 포함하고 있다면! (아래에 있는 as 문법도 수정하기)
newData[(d as unknown as {doc: Id<"documents">}).doc] = parsedDate;
}
});
});
setOrientedData(newData);
}
}, [connectedCalendar]);

const onDragOver = (e: React.DragEvent) => {
if (e.dataTransfer.types[0] === "elementid") {
e.preventDefault();
Expand Down Expand Up @@ -94,6 +116,7 @@ const BoardView = ({
editable={editable}
documents={documents}
onDragChange={(e) => onElementDragOver(e, i)}
calendarData={orientedData}
/>
))}
</ArrayDragSpace>
Expand Down
61 changes: 61 additions & 0 deletions components/KanbanBoard/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Id } from "@/convex/_generated/dataModel";
export type Calendar = CalendarDocumentElement[];

export type CalendarDocumentElement = {
_id: string;
name: string;
content: CalendarDocument[];
calendarIndex: number;
calendarMonth: number;
};

//calendar에 들어갈 내용
export type CalendarDocument = {
_id: string;
name?: string;
content?: [];
};

export const newCalendarDocument = (...names: string[]): Calendar => {
return names.map((v) => {
const currentDate = new Date();
const formattedDate = `${currentDate.getMonth() + 2}`;

return {
_id: generateId(),
name: v,
content: [],
calendarIndex: +v,
calendarMonth: +formattedDate,
};
});
};

export function generateId() {
var S4 = function () {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
};
return `${S4()}${S4()}${S4()}${S4()}`;
}

export function parseDateByIndex(month: number, index: number): Date {
const today = new Date();

const thisYear = new Date(today.getFullYear(), month - 1, 1);
thisYear.setDate(thisYear.getDate() - thisYear.getDay() + index);

// 연도가 구분되어 있지 않고, '목표' 이므로 시간이 일주일을 넘기면 다음 해의 날짜를 대신 카운트
if (getDayDifference(today, thisYear) > -7) return thisYear;

const nextYear = new Date(today.getFullYear() + 1, month - 1, 1);
nextYear.setDate(nextYear.getDate() - nextYear.getDay() + index);

return nextYear;
}

function getDayDifference(date1: Date, date2: Date) {
const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime());
const millisecondsPerDay = 1000 * 60 * 60 * 24;
const daysDifference = Math.floor(diffInMilliseconds / millisecondsPerDay);
return daysDifference;
}
5 changes: 3 additions & 2 deletions components/array-drag-space.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { cn } from "@/lib/utils";
import React from "react";

export const ArrayDragSpace = ({
children,
Expand Down Expand Up @@ -35,10 +36,10 @@ export const ArrayDragSpace = ({
if (!children) return null;

return (<>
{children.map((v, i) => (<>
{children.map((v, i) => (<React.Fragment key={i}>
<Space onDragOver={(e) => onDragToIndex?.(e, i)} enabled={index === i} />
{v}
</>)
</React.Fragment>)
)}
<Space onDragOver={(e) => onDragToLast?.(e)} enabled={index === children.length} />
</>)
Expand Down
23 changes: 22 additions & 1 deletion types/calendar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Id } from "@/convex/_generated/dataModel";

export type Calendar = CalendarDocumentElement[];

export type CalendarDocumentElement = {
Expand Down Expand Up @@ -38,3 +37,25 @@ export function generateId() {
};
return `${S4()}${S4()}${S4()}${S4()}`;
}

export function parseDateByIndex(month: number, index: number): Date {
const today = new Date();

const thisYear = new Date(today.getFullYear(), month - 1, 1);
thisYear.setDate(thisYear.getDate() - thisYear.getDay() + index);

// 연도가 구분되어 있지 않고, '목표' 이므로 시간이 일주일을 넘기면 다음 해의 날짜를 대신 카운트
if (getDayDifference(today, thisYear) > -7) return thisYear;

const nextYear = new Date(today.getFullYear() + 1, month - 1, 1);
nextYear.setDate(nextYear.getDate() - nextYear.getDay() + index);

return nextYear;
}

function getDayDifference(date1: Date, date2: Date) {
const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime());
const millisecondsPerDay = 1000 * 60 * 60 * 24;
const daysDifference = Math.floor(diffInMilliseconds / millisecondsPerDay);
return daysDifference;
}