diff --git a/client/package-lock.json b/client/package-lock.json
index aaa77f5e..fcf79b1c 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -130,7 +130,6 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -2124,7 +2123,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18"
},
@@ -2148,7 +2146,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -4904,7 +4901,6 @@
"resolved": "https://registry.npmjs.org/@stream-io/video-client/-/video-client-1.43.0.tgz",
"integrity": "sha512-DIP24SzyvvhCZ0ac6jrZ2ks4G9KWYCySlVGaRrRGVpsqQNINW6tOXXuKCCoZAQPlv279M3Jt1hf/fQjZMqNtvg==",
"license": "See license in LICENSE",
- "peer": true,
"dependencies": {
"@protobuf-ts/runtime": "^2.11.1",
"@protobuf-ts/runtime-rpc": "^2.11.1",
@@ -5342,7 +5338,8 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -5464,7 +5461,6 @@
"integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -5475,7 +5471,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -5619,7 +5614,6 @@
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
@@ -6547,7 +6541,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -7119,7 +7112,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -7273,7 +7265,6 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@kurkle/color": "^0.3.0"
},
@@ -7743,7 +7734,8 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/dotenv": {
"version": "17.2.3",
@@ -8128,7 +8120,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8189,7 +8180,6 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@@ -11327,7 +11317,6 @@
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"cssstyle": "^4.2.1",
"data-urls": "^5.0.0",
@@ -11903,6 +11892,7 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -12723,7 +12713,6 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -12869,7 +12858,6 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -13066,6 +13054,7 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -13081,6 +13070,7 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=10"
},
@@ -13173,7 +13163,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -13193,7 +13182,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -13206,7 +13194,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz",
"integrity": "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -13223,7 +13210,8 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/react-refresh": {
"version": "0.18.0",
@@ -14409,7 +14397,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -14658,7 +14645,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -14902,7 +14888,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -14994,7 +14979,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -15475,7 +15459,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/client/src/components/appointmentCard/AppointmentCard.tsx b/client/src/components/appointmentCard/AppointmentCard.tsx
index e377c3f0..7a941eed 100644
--- a/client/src/components/appointmentCard/AppointmentCard.tsx
+++ b/client/src/components/appointmentCard/AppointmentCard.tsx
@@ -57,6 +57,7 @@ export const AppointmentCard = ({
lesson={appointment.lesson}
teacherName={appointment.teacherName}
price={appointment.price}
+ description={appointment.description}
weeklySchedule={appointment.weeklySchedule}
isRegularTeacher={isRegularTeacher}
/>
diff --git a/client/src/components/appointmentCard/AppointmentInfo.tsx b/client/src/components/appointmentCard/AppointmentInfo.tsx
index bb0b1e8f..d340462f 100644
--- a/client/src/components/appointmentCard/AppointmentInfo.tsx
+++ b/client/src/components/appointmentCard/AppointmentInfo.tsx
@@ -4,6 +4,7 @@ export type AppointmentInfoProps = {
lesson?: string;
teacherName?: string;
price: string;
+ description?: string;
weeklySchedule?: WeeklyScheduleSlot[];
isRegularTeacher?: boolean;
};
@@ -33,6 +34,7 @@ export const AppointmentInfo = ({
lesson,
teacherName,
price,
+ description,
weeklySchedule,
isRegularTeacher = false,
}: AppointmentInfoProps) => {
@@ -50,6 +52,12 @@ export const AppointmentInfo = ({
{teacherName || "Teacher"}
+ {description && (
+
+ {description}
+
+ )}
+
{price} euro /hour
diff --git a/client/src/components/bookingConfirmation/BookingConfirmation.tsx b/client/src/components/bookingConfirmation/BookingConfirmation.tsx
index c0660e07..43a3257b 100644
--- a/client/src/components/bookingConfirmation/BookingConfirmation.tsx
+++ b/client/src/components/bookingConfirmation/BookingConfirmation.tsx
@@ -28,6 +28,7 @@ export const BookingConfirmation = ({
selectedSubject,
selectedLevel,
selectedPrice,
+ description,
onSuccess: onSuccessCallback,
} = payload;
@@ -52,6 +53,7 @@ export const BookingConfirmation = ({
"General Lesson",
price: selectedPrice?.toString() || teacher.priceFrom?.toString() || "0",
level: selectedLevel || "",
+ description: description || "",
};
createAppointment(appointmentData, {
@@ -93,6 +95,11 @@ export const BookingConfirmation = ({
Price: €{selectedPrice || teacher?.priceFrom}
+ {description && (
+
+ Description: {description}
+
+ )}
The lesson request will be sent to the teacher.
diff --git a/client/src/components/regularStudentScheduleModal/RegularStudentScheduleModal.tsx b/client/src/components/regularStudentScheduleModal/RegularStudentScheduleModal.tsx
index 913e34da..2fe4fabd 100644
--- a/client/src/components/regularStudentScheduleModal/RegularStudentScheduleModal.tsx
+++ b/client/src/components/regularStudentScheduleModal/RegularStudentScheduleModal.tsx
@@ -3,6 +3,7 @@ import { Button } from "../ui/button/Button";
import Cross from "../icons/Cross";
import { SelectComponent } from "../ui/select/Select";
import { WeeklyScheduleSlot } from "../../types/appointments.types";
+import { useModalStore } from "../../store/modals.store";
interface RegularStudentScheduleModalProps {
isOpen: boolean;
@@ -40,6 +41,7 @@ const ModalContent = ({
useState(initialSavedSlots);
const [currentDay, setCurrentDay] = useState("Monday");
const [currentHour, setCurrentHour] = useState(7);
+ const { open: openModal } = useModalStore();
const isDuplicate = savedSlots.some(
(slot) => slot.day === currentDay && slot.hour === currentHour,
@@ -78,6 +80,15 @@ const ModalContent = ({
};
const handleSave = () => {
+ if (!hasScheduleChanged) {
+ openModal("alert", {
+ title: "No Changes Made",
+ message:
+ "Please click on 'Add to Schedule' button to add lessons before saving.",
+ });
+ return;
+ }
+
onSave(savedSlots);
onClose();
};
@@ -86,6 +97,27 @@ const ModalContent = ({
onClose();
};
+ const hasScheduleChanged = (() => {
+ if (savedSlots.length !== initialSavedSlots.length) {
+ return true;
+ }
+
+ const sortSlots = (slots: WeeklyScheduleSlot[]) =>
+ [...slots].sort((a, b) => {
+ if (a.day !== b.day) return a.day.localeCompare(b.day);
+ return a.hour - b.hour;
+ });
+
+ const sortedSaved = sortSlots(savedSlots);
+ const sortedInitial = sortSlots(initialSavedSlots);
+
+ return !sortedSaved.every(
+ (slot, index) =>
+ slot.day === sortedInitial[index]?.day &&
+ slot.hour === sortedInitial[index]?.hour,
+ );
+ })();
+
return (
diff --git a/client/src/components/teacherAppointmentCard/TeacherAppointmentCard.tsx b/client/src/components/teacherAppointmentCard/TeacherAppointmentCard.tsx
index db78e4c5..9dc8a0c0 100644
--- a/client/src/components/teacherAppointmentCard/TeacherAppointmentCard.tsx
+++ b/client/src/components/teacherAppointmentCard/TeacherAppointmentCard.tsx
@@ -59,6 +59,7 @@ export const TeacherAppointmentCard = ({
lesson={appointment.lesson}
studentName={appointment.studentName}
price={appointment.price}
+ description={appointment.description}
weeklySchedule={appointment.weeklySchedule}
isRegularTab={isRegularTab}
/>
diff --git a/client/src/components/teacherAppointmentCard/TeacherAppointmentInfo.tsx b/client/src/components/teacherAppointmentCard/TeacherAppointmentInfo.tsx
index 1459368a..429eb86c 100644
--- a/client/src/components/teacherAppointmentCard/TeacherAppointmentInfo.tsx
+++ b/client/src/components/teacherAppointmentCard/TeacherAppointmentInfo.tsx
@@ -4,6 +4,7 @@ export type TeacherAppointmentInfoProps = {
lesson?: string;
studentName?: string;
price: string;
+ description?: string;
weeklySchedule?: WeeklyScheduleSlot[];
isRegularTab?: boolean;
};
@@ -33,6 +34,7 @@ export const TeacherAppointmentInfo = ({
lesson,
studentName,
price,
+ description,
weeklySchedule,
isRegularTab = false,
}: TeacherAppointmentInfoProps) => {
@@ -54,6 +56,12 @@ export const TeacherAppointmentInfo = ({
{price} euro /hour
+ {description && (
+
+ {description}
+
+ )}
+
{isRegularTab && weeklySchedule && weeklySchedule.length > 0 && (
{formatSchedule(weeklySchedule)}
diff --git a/client/src/components/teacherSection/teacherSchedule/TeacherSchedule.tsx b/client/src/components/teacherSection/teacherSchedule/TeacherSchedule.tsx
index 308c7761..766fe76f 100644
--- a/client/src/components/teacherSection/teacherSchedule/TeacherSchedule.tsx
+++ b/client/src/components/teacherSection/teacherSchedule/TeacherSchedule.tsx
@@ -7,6 +7,7 @@ import { useAuthSessionStore } from "../../../store/authSession.store";
import { useTeacherAppointmentsQuery } from "../../../features/appointments/query/useTeacherAppointmentsQuery";
import { SelectComponent } from "../../ui/select/Select.tsx";
import { Button } from "../../ui/button/Button";
+import { getDescriptionValidation } from "../../../utils/appointmentDescription.validation";
interface TeacherScheduleProps {
teacher?: TeacherType;
@@ -18,6 +19,7 @@ export default function TeacherSchedule({ teacher }: TeacherScheduleProps) {
const [selectedSubject, setSelectedSubject] = useState("");
const [selectedLevel, setSelectedLevel] = useState("");
const [selectedTime, setSelectedTime] = useState(null);
+ const [description, setDescription] = useState("");
const [showSubjectLevelSelection, setShowSubjectLevelSelection] =
useState(false);
@@ -87,6 +89,12 @@ export default function TeacherSchedule({ teacher }: TeacherScheduleProps) {
return levelItem.price;
}, [selectedSubject, selectedLevel, teacher]);
+ const descriptionValidation = useMemo(() => {
+ return getDescriptionValidation(description);
+ }, [description]);
+
+ const { wordCount, isValid: isDescriptionValid } = descriptionValidation;
+
const handleDateSelection = (date: Date): void => {
setSelectedDate(date);
setShowTimeAndBook(true);
@@ -128,6 +136,7 @@ export default function TeacherSchedule({ teacher }: TeacherScheduleProps) {
selectedSubject,
selectedLevel,
selectedPrice: selectedPrice || undefined,
+ description,
onSuccess: () => {
setSelectedDate(null);
setShowTimeAndBook(false);
@@ -135,6 +144,7 @@ export default function TeacherSchedule({ teacher }: TeacherScheduleProps) {
setShowSubjectLevelSelection(false);
setSelectedSubject("");
setSelectedLevel("");
+ setDescription("");
},
});
}
@@ -313,11 +323,45 @@ export default function TeacherSchedule({ teacher }: TeacherScheduleProps) {
)}
+ {selectedSubject && selectedLevel && (
+
+
+
+ )}
+
diff --git a/client/src/features/appointments/mutations/useCreateAppointmentMutation.ts b/client/src/features/appointments/mutations/useCreateAppointmentMutation.ts
index f2a439be..337956f9 100644
--- a/client/src/features/appointments/mutations/useCreateAppointmentMutation.ts
+++ b/client/src/features/appointments/mutations/useCreateAppointmentMutation.ts
@@ -12,6 +12,7 @@ interface CreateAppointmentRequest {
time: string;
lesson: string;
price: string;
+ description?: string;
}
const createAppointment = async (
diff --git a/client/src/store/modals.store.ts b/client/src/store/modals.store.ts
index f23abcb1..ad099281 100644
--- a/client/src/store/modals.store.ts
+++ b/client/src/store/modals.store.ts
@@ -19,6 +19,7 @@ type ModalPayload = {
selectedSubject?: string;
selectedLevel?: string;
selectedPrice?: number;
+ description?: string;
onSuccess?: () => void;
};
signIn?: never;
diff --git a/client/src/types/appointments.types.ts b/client/src/types/appointments.types.ts
index 4d9e071c..dd28f4e6 100644
--- a/client/src/types/appointments.types.ts
+++ b/client/src/types/appointments.types.ts
@@ -12,6 +12,7 @@ export interface Appointment {
price: string;
date: string;
time: string;
+ description?: string;
status: AppointmentStatus;
videoCall?: string;
isRegularStudent?: boolean;
diff --git a/client/src/utils/appointmentDescription.validation.ts b/client/src/utils/appointmentDescription.validation.ts
new file mode 100644
index 00000000..5e2266c1
--- /dev/null
+++ b/client/src/utils/appointmentDescription.validation.ts
@@ -0,0 +1,24 @@
+export const MAX_DESCRIPTION_WORDS = 50;
+
+export const countWords = (text: string): number => {
+ return text
+ .trim()
+ .split(/\s+/)
+ .filter((word) => word.length > 0).length;
+};
+
+export const isDescriptionValid = (description: string): boolean => {
+ const wordCount = countWords(description);
+ return wordCount <= MAX_DESCRIPTION_WORDS;
+};
+
+export const getDescriptionValidation = (description: string) => {
+ const wordCount = countWords(description);
+ const isValid = wordCount <= MAX_DESCRIPTION_WORDS;
+
+ return {
+ wordCount,
+ isValid,
+ maxWords: MAX_DESCRIPTION_WORDS,
+ };
+};
diff --git a/server/package-lock.json b/server/package-lock.json
index ae3a557f..537833cf 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -88,7 +88,6 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -3390,7 +3389,6 @@
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -3691,7 +3689,6 @@
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
@@ -4637,7 +4634,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5258,7 +5254,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -6368,7 +6363,6 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -6445,7 +6439,6 @@
"integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@@ -6509,7 +6502,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -11166,7 +11158,6 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -12576,7 +12567,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -12850,7 +12840,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/server/src/db/schemes/appointmentSchema.ts b/server/src/db/schemes/appointmentSchema.ts
index 0d5e2780..6b2e6eaa 100644
--- a/server/src/db/schemes/appointmentSchema.ts
+++ b/server/src/db/schemes/appointmentSchema.ts
@@ -22,6 +22,7 @@ export const AppointmentSchema = new mongoose.Schema(
price: { type: String, required: true },
date: { type: String, required: true },
time: { type: String, required: true },
+ description: { type: String, default: "" },
status: {
type: String,
required: true,
diff --git a/server/src/db/schemes/types/appointment.types.ts b/server/src/db/schemes/types/appointment.types.ts
index 2ea51c40..bf7db7f9 100644
--- a/server/src/db/schemes/types/appointment.types.ts
+++ b/server/src/db/schemes/types/appointment.types.ts
@@ -14,6 +14,7 @@ export interface AppointmentTypeDB {
price: string;
date: string;
time: string;
+ description?: string;
status: "pending" | "approved" | "rejected";
videoCall?: string;
isRegularStudent?: boolean;
diff --git a/server/src/routes/uploadRoute.ts b/server/src/routes/uploadRoute.ts
index bca18150..d54ccc46 100644
--- a/server/src/routes/uploadRoute.ts
+++ b/server/src/routes/uploadRoute.ts
@@ -9,7 +9,7 @@ const router = Router();
const uploadController = new UploadController();
const authMiddleware = container.get(TYPES.AuthMiddleware);
-// POST /api/upload/avatar - Завантажити/змінити аватар
+// POST /api/upload/avatar - Upload/change avatar
router.post(
"/avatar",
authMiddleware.handle,
@@ -17,7 +17,7 @@ router.post(
(req, res) => uploadController.uploadAvatar(req, res),
);
-// DELETE /api/upload/avatar - Видалити аватар
+// DELETE /api/upload/avatar - Delete avatar
router.delete("/avatar", authMiddleware.handle, (req, res) =>
uploadController.deleteAvatar(req, res),
);
diff --git a/server/src/services/appointment/appointment.service.ts b/server/src/services/appointment/appointment.service.ts
index df09bc48..e792d4a3 100644
--- a/server/src/services/appointment/appointment.service.ts
+++ b/server/src/services/appointment/appointment.service.ts
@@ -73,6 +73,7 @@ export class AppointmentService {
price: data.price,
date: data.date,
time: data.time,
+ description: data.description || "",
status: "pending" as const,
videoCall: `https://meet.google.com/${data.teacherId}-${data.studentId}-${Date.now()}`,
isRegularStudent: false,
@@ -303,6 +304,7 @@ export class AppointmentService {
price: string | number;
date: string;
time: string;
+ description?: string;
status: string;
videoCall?: string;
isRegularStudent?: boolean;
@@ -325,6 +327,7 @@ export class AppointmentService {
price: priceStr,
date: apt.date,
time: apt.time,
+ description: apt.description,
status: apt.status,
videoCall: apt.videoCall,
isRegularStudent: apt.isRegularStudent,
diff --git a/server/src/types/appointment/appointment.types.ts b/server/src/types/appointment/appointment.types.ts
index 3a642647..d4ae26d6 100644
--- a/server/src/types/appointment/appointment.types.ts
+++ b/server/src/types/appointment/appointment.types.ts
@@ -10,6 +10,7 @@ export interface CreateAppointmentType {
price: string;
date: string;
time: string;
+ description?: string;
}
export interface UpdateAppointmentStatusType {