You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.6 KiB
TypeScript
131 lines
3.6 KiB
TypeScript
// frontend/components/journey/FlowCard.tsx — 흐름 카드 (routine/task/meet, 원본 fcard)
|
|
import { forwardRef } from "react";
|
|
import { Icon } from "@/components/Icon";
|
|
import type { IconName } from "@/components/icons/paths";
|
|
import type { RoutineCard, TaskCard, MeetCard, JourneyPerson } from "@/lib/types";
|
|
|
|
type Hover = {
|
|
className: string;
|
|
onMouseEnter: () => void;
|
|
onMouseLeave: () => void;
|
|
};
|
|
|
|
const FALLBACK_PERSON: Pick<JourneyPerson, "name" | "initial" | "color"> = {
|
|
name: "",
|
|
initial: "?",
|
|
color: "var(--card-2)",
|
|
};
|
|
|
|
// 1 — 아침 준비 (routine)
|
|
export const RoutineFlowCard = forwardRef<HTMLDivElement, { card: RoutineCard; hover: Hover }>(
|
|
function RoutineFlowCard({ card, hover }, ref) {
|
|
return (
|
|
<div
|
|
className={hover.className}
|
|
ref={ref}
|
|
onMouseEnter={hover.onMouseEnter}
|
|
onMouseLeave={hover.onMouseLeave}
|
|
>
|
|
<div className="fcard-row">
|
|
<div className="fc-ic">
|
|
<Icon name={card.icon as IconName} />
|
|
</div>
|
|
<div className="fc-main">
|
|
<div className="fc-title">{card.title}</div>
|
|
<div className="fc-meta">{card.meta}</div>
|
|
</div>
|
|
<div className={"mini " + (card.toolTone || "")}>
|
|
<Icon name={card.tool as IconName} w={2.4} />
|
|
</div>
|
|
</div>
|
|
<div className="fcard-foot">
|
|
<span className="tag">{card.tag}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
);
|
|
|
|
// 2 — 오전 집중 (task, 체크 토글)
|
|
export const TaskFlowCard = forwardRef<
|
|
HTMLDivElement,
|
|
{
|
|
card: TaskCard;
|
|
person?: JourneyPerson;
|
|
done: boolean;
|
|
onToggle: () => void;
|
|
hover: Hover;
|
|
}
|
|
>(function TaskFlowCard({ card, person, done, onToggle, hover }, ref) {
|
|
const pe = person ?? FALLBACK_PERSON;
|
|
return (
|
|
<div
|
|
className={hover.className}
|
|
ref={ref}
|
|
onMouseEnter={hover.onMouseEnter}
|
|
onMouseLeave={hover.onMouseLeave}
|
|
>
|
|
<div className="fcard-row">
|
|
<div
|
|
className="fcheck"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onToggle();
|
|
}}
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-pressed={done}
|
|
aria-label={card.title}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
onToggle();
|
|
}
|
|
}}
|
|
>
|
|
<Icon name="tick" w={3} />
|
|
</div>
|
|
<div className="fc-main">
|
|
<div className="fc-title">{card.title}</div>
|
|
<div className="fc-meta">{card.meta}</div>
|
|
</div>
|
|
<div className="fc-who" style={{ background: pe.color }} title={pe.name}>
|
|
{pe.initial}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
// 3 — 오후 협업 (meet)
|
|
export const MeetFlowCard = forwardRef<
|
|
HTMLDivElement,
|
|
{ card: MeetCard; person?: JourneyPerson; hover: Hover }
|
|
>(function MeetFlowCard({ card, person, hover }, ref) {
|
|
const pe = person ?? FALLBACK_PERSON;
|
|
return (
|
|
<div
|
|
className={hover.className}
|
|
ref={ref}
|
|
onMouseEnter={hover.onMouseEnter}
|
|
onMouseLeave={hover.onMouseLeave}
|
|
>
|
|
<div className="fcard-row">
|
|
<div className="fc-av" style={{ background: pe.color }}>
|
|
{pe.initial}
|
|
</div>
|
|
<div className="fc-main">
|
|
<div className="fc-title">{card.title}</div>
|
|
<div className="fc-meta">{card.meta}</div>
|
|
</div>
|
|
{card.soon ? (
|
|
<span className="mini soon">곧</span>
|
|
) : (
|
|
<div className="fc-time">{card.time}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|