You are an expert software engineer specialised in Next.js 14 with TypeScript and TailwindCSS. I need you to complete me `handleChange` and `handleSave` functions and implement them in my `Feedback` component.

app/submissions/[submissionUrl]/mark/[assessmentId]/feedbacks.tsx

```
"use client";

import { IQuestionBranch } from "@/app/actions/server/default.action";
import useVisibleElements from "@/hooks/visible-elements.hook";
import React, { useCallback, useMemo, useState } from "react";
import Feedback from "./feedback";
import { IContent } from "@/database/schemas/content.schema";
import { createHierarchicalLabel } from "@/lib/utils";
import { AnimatePresence } from "framer-motion";

interface IProps {
  question: IQuestionBranch;
  dependency: { [key: string]: string };
}

const Feedbacks = ({ question, dependency }: IProps) => {
  const visibleElementIds = useVisibleElements();

  const [feedbacks, setFeedbacks] = useState(initFeedbacks(question));
  const [activeFeedback, setActiveFeedback] = useState<string | null>(null);

  const visibleQuestionIds = useMemo(() => getVisibleQuestionIds(dependency, visibleElementIds), [visibleElementIds, dependency]);

  const visibleFeedbacks = feedbacks.filter((el) => visibleQuestionIds.includes(el.questionId));

  const handleChange = useCallback((questionId: string, grade?: string, feedback?: IContent[]) => {
    /**
     * TODO:
     *  - Update state
     *  - Initialise timeout for saving the feedback
     *  - Cancel any ongoing timeouts first
     *  - Call the save function after 2 seconds
     */
  }, []);

  const handleSave = useCallback(() => {
    /**
     * TODO:
     *  - Simulate a delay for saving (to be replaced by an actual server action)
     *  - Clear timeout after
     */
  }, []);

  return (
    <AnimatePresence>
      {visibleFeedbacks.map((el) => (
        <Feedback
          key={`question-${el.questionId}-feedback`}
          isActive={activeFeedback === el.questionId}
          onClick={() => setActiveFeedback((prev) => (prev === el.questionId ? null : el.questionId))}
          {...el}
        />
      ))}
    </AnimatePresence>
  );
};

export default Feedbacks;

export interface IFeedback {
  questionId: string;
  label: string;
  grade: string;
  feedback: IContent[];
  timeoutId: NodeJS.Timeout | null;
}

const initFeedbacks = (question: IQuestionBranch): IFeedback[] => {
  const newFeedbacks: IFeedback[] = [];
  const addFeedback = (feedbacks: IFeedback[], questionBranch: IQuestionBranch, trail: number[] = []): void => {
    if (questionBranch.grade === undefined || !questionBranch.feedback) throw new Error("Invalid question branch data");
    feedbacks.push({
      questionId: questionBranch.id,
      label: trail.length > 0 ? `Question ${createHierarchicalLabel(trail)}` : "Assessment",
      grade: questionBranch.grade,
      feedback: questionBranch.feedback,
      timeoutId: null,
    });
    for (let i = 0; i < questionBranch.questions.length; i++) {
      addFeedback(feedbacks, questionBranch.questions[i], [...trail, i]);
    }
  };
  addFeedback(newFeedbacks, question);
  return newFeedbacks;
};

const getVisibleQuestionIds = (dependency: { [key: string]: string }, visibleElementIds: string[]): string[] => {
  // Convert the visibleElementIds array to a Set for efficient lookups
  const visibleSet = new Set(visibleElementIds);

  // Iterate over the keys of the dependency object
  // and filter keys whose corresponding value is in the visibleSet
  return Object.keys(dependency).filter((key) => visibleSet.has(dependency[key]));
};
```

app/submissions/[submissionUrl]/mark/[assessmentId]/feedback.tsx

```
import ContentsView from "@/components/c-ui/contents/view";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import React from "react";
import { IFeedback } from "./feedbacks";
import { IContent } from "@/database/schemas/content.schema";
import { motion } from "framer-motion";
import { LoaderCircleIcon, PencilIcon, PencilLineIcon, PlusIcon } from "lucide-react";

interface IProps extends IFeedback {
  isActive: boolean;
  onClick: () => void;
}

const Feedback = ({ questionId, label, grade, feedback, timeoutId, isActive, onClick }: IProps) => {
  const state: "empty" | "preview" | "active" | "saving" = !!timeoutId
    ? "saving"
    : isActive
    ? "active"
    : !grade && !feedback.length
    ? "empty"
    : "preview";

  let description: React.JSX.Element;

  switch (state) {
    case "empty":
      description = (
        <>
          <PlusIcon className="h-4 w-4" />
          <p>Add feedback</p>
        </>
      );
      break;
    case "preview":
      description = (
        <>
          <PencilIcon className="h-4 w-4" />
          <p>Edit feedback</p>
        </>
      );
      break;
    case "active":
      description = (
        <>
          <PencilLineIcon className="h-4 w-4" />
          <p>Updating feedback</p>
        </>
      );
      break;
    case "saving":
      description = (
        <>
          <LoaderCircleIcon className="h-4 w-4 animate-spin" />
          <p>Saving changes</p>
        </>
      );
      break;
    default:
      throw new Error("");
  }

  return (
    <motion.div
      initial={{ opacity: 0, x: -50 }} // Slide in from left
      animate={{ opacity: 1, x: 0 }} // Slide to original position
      exit={{ opacity: 0, x: 50 }} // Slide out to right
      transition={{ duration: 0.3, ease: "easeOut" }} // Customize as needed
      layout // Optional: Enables smooth layout transitions
    >
      <Card className={`${isActive ? "shadow-md" : "opacity-60 hover:opacity-100 cursor-pointer"}`} onClick={onClick}>
        <CardHeader>
          <CardTitle>{label}</CardTitle>
          <CardDescription className="flex gap-2 items-center">{description}</CardDescription>
        </CardHeader>
        {state === "preview" && <Preview questionId={questionId} grade={grade} feedback={feedback} />}
      </Card>
    </motion.div>
  );
};

export default Feedback;

interface IPreviewProps {
  questionId: string;
  grade: string;
  feedback: IContent[];
}

const Preview = ({ questionId, grade, feedback }: IPreviewProps) => {
  return (
    <CardContent className="h-fit w-full flex flex-col">
      <p>
        Grade: <span>{grade}</span>
      </p>
      <p>Feedback:</p>
      <ContentsView keyPrefix={`question-${questionId}-feedback`} contents={feedback} />
    </CardContent>
  );
};

interface IFormProps {
  questionId: string;
  grade: string;
  feedback: IContent[];
  disabled: boolean;
}

const Form = ({}: IFormProps) => {
  return null;
};
```