From ec9249d5ee7620171ef763c9a09312e08bf1a427 Mon Sep 17 00:00:00 2001 From: Veirt Date: Sun, 8 Dec 2024 19:19:11 +0800 Subject: [PATCH] feat(reviews): track review prompt dismissed status --- drizzle/0018_short_ikaris.sql | 1 + drizzle/meta/0018_snapshot.json | 1117 ++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + src/db/schema.ts | 1 + src/module/review/review.controller.ts | 44 +- src/module/review/review.repository.ts | 9 +- src/module/review/review.route.ts | 13 +- src/module/review/review.schema.ts | 6 + src/module/review/review.service.ts | 4 + 9 files changed, 1199 insertions(+), 3 deletions(-) create mode 100644 drizzle/0018_short_ikaris.sql create mode 100644 drizzle/meta/0018_snapshot.json diff --git a/drizzle/0018_short_ikaris.sql b/drizzle/0018_short_ikaris.sql new file mode 100644 index 0000000..ee5a082 --- /dev/null +++ b/drizzle/0018_short_ikaris.sql @@ -0,0 +1 @@ +ALTER TABLE "orders" ADD COLUMN "review_dismissed_at" timestamp; \ No newline at end of file diff --git a/drizzle/meta/0018_snapshot.json b/drizzle/meta/0018_snapshot.json new file mode 100644 index 0000000..e0e88f2 --- /dev/null +++ b/drizzle/meta/0018_snapshot.json @@ -0,0 +1,1117 @@ +{ + "id": "1c6b9ac0-0488-416d-8ff5-1ffc4e6772ab", + "prevId": "ffb3216d-1b2c-4378-b05d-512053f5ab02", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "icon_url": { + "name": "icon_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_messages": { + "name": "chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "roomId": { + "name": "roomId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "senderId": { + "name": "senderId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sender_role": { + "name": "sender_role", + "type": "user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "message_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "chat_messages_room_sent_idx": { + "name": "chat_messages_room_sent_idx", + "columns": [ + { + "expression": "roomId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sent_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_sender_idx": { + "name": "chat_messages_sender_idx", + "columns": [ + { + "expression": "senderId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_messages_roomId_chat_rooms_id_fk": { + "name": "chat_messages_roomId_chat_rooms_id_fk", + "tableFrom": "chat_messages", + "tableTo": "chat_rooms", + "columnsFrom": [ + "roomId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_rooms": { + "name": "chat_rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "learnerId": { + "name": "learnerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tutorId": { + "name": "tutorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_rooms_learnerId_learners_id_fk": { + "name": "chat_rooms_learnerId_learners_id_fk", + "tableFrom": "chat_rooms", + "tableTo": "learners", + "columnsFrom": [ + "learnerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chat_rooms_tutorId_tutors_id_fk": { + "name": "chat_rooms_tutorId_tutors_id_fk", + "tableFrom": "chat_rooms", + "tableTo": "tutors", + "columnsFrom": [ + "tutorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.fcm_tokens": { + "name": "fcm_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "fcm_tokens_userId_token_index": { + "name": "fcm_tokens_userId_token_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "fcm_tokens_userId_index": { + "name": "fcm_tokens_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.interests": { + "name": "interests", + "schema": "", + "columns": { + "learnerId": { + "name": "learnerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "categoryId": { + "name": "categoryId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "interests_learnerId_learners_id_fk": { + "name": "interests_learnerId_learners_id_fk", + "tableFrom": "interests", + "tableTo": "learners", + "columnsFrom": [ + "learnerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "interests_categoryId_categories_id_fk": { + "name": "interests_categoryId_categories_id_fk", + "tableFrom": "interests", + "tableTo": "categories", + "columnsFrom": [ + "categoryId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.learners": { + "name": "learners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "learning_style": { + "name": "learning_style", + "type": "learning_style", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "gender": { + "name": "gender", + "type": "gender", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "phone_number": { + "name": "phone_number", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "district": { + "name": "district", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "learners_email_unique": { + "name": "learners_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.orders": { + "name": "orders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "learnerId": { + "name": "learnerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tutoriesId": { + "name": "tutoriesId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "session_time": { + "name": "session_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "estimated_end_time": { + "name": "estimated_end_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "total_hours": { + "name": "total_hours", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type_lesson": { + "name": "type_lesson", + "type": "type_lesson", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "review_dismissed_at": { + "name": "review_dismissed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "orders_learnerId_index": { + "name": "orders_learnerId_index", + "columns": [ + { + "expression": "learnerId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orders_tutoriesId_index": { + "name": "orders_tutoriesId_index", + "columns": [ + { + "expression": "tutoriesId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orders_status_index": { + "name": "orders_status_index", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orders_session_time_index": { + "name": "orders_session_time_index", + "columns": [ + { + "expression": "session_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orders_status_created_at_index": { + "name": "orders_status_created_at_index", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "orders_learnerId_status_index": { + "name": "orders_learnerId_status_index", + "columns": [ + { + "expression": "learnerId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "orders_learnerId_learners_id_fk": { + "name": "orders_learnerId_learners_id_fk", + "tableFrom": "orders", + "tableTo": "learners", + "columnsFrom": [ + "learnerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "orders_tutoriesId_tutories_id_fk": { + "name": "orders_tutoriesId_tutories_id_fk", + "tableFrom": "orders", + "tableTo": "tutories", + "columnsFrom": [ + "tutoriesId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reviews": { + "name": "reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "orderId": { + "name": "orderId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reviews_rating_index": { + "name": "reviews_rating_index", + "columns": [ + { + "expression": "rating", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reviews_created_at_index": { + "name": "reviews_created_at_index", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reviews_orderId_orders_id_fk": { + "name": "reviews_orderId_orders_id_fk", + "tableFrom": "reviews", + "tableTo": "orders", + "columnsFrom": [ + "orderId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "reviews_orderId_unique": { + "name": "reviews_orderId_unique", + "nullsNotDistinct": false, + "columns": [ + "orderId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tutories": { + "name": "tutories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tutorId": { + "name": "tutorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "categoryId": { + "name": "categoryId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "about_you": { + "name": "about_you", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "teaching_methodology": { + "name": "teaching_methodology", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hourly_rate": { + "name": "hourly_rate", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type_lesson": { + "name": "type_lesson", + "type": "type_lesson", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "tutories_hourly_rate_index": { + "name": "tutories_hourly_rate_index", + "columns": [ + { + "expression": "hourly_rate", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tutories_type_lesson_index": { + "name": "tutories_type_lesson_index", + "columns": [ + { + "expression": "type_lesson", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tutories_created_at_index": { + "name": "tutories_created_at_index", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tutories_tutorId_categoryId_index": { + "name": "tutories_tutorId_categoryId_index", + "columns": [ + { + "expression": "tutorId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "categoryId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tutories_tutorId_tutors_id_fk": { + "name": "tutories_tutorId_tutors_id_fk", + "tableFrom": "tutories", + "tableTo": "tutors", + "columnsFrom": [ + "tutorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tutories_categoryId_categories_id_fk": { + "name": "tutories_categoryId_categories_id_fk", + "tableFrom": "tutories", + "tableTo": "categories", + "columnsFrom": [ + "categoryId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tutors": { + "name": "tutors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "gender": { + "name": "gender", + "type": "gender", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "phone_number": { + "name": "phone_number", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "district": { + "name": "district", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "availability": { + "name": "availability", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tutors_email_unique": { + "name": "tutors_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.gender": { + "name": "gender", + "schema": "public", + "values": [ + "male", + "female", + "prefer not to say" + ] + }, + "public.learning_style": { + "name": "learning_style", + "schema": "public", + "values": [ + "visual", + "auditory", + "kinesthetic" + ] + }, + "public.message_type": { + "name": "message_type", + "schema": "public", + "values": [ + "text", + "image" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "pending", + "declined", + "scheduled", + "completed" + ] + }, + "public.type_lesson": { + "name": "type_lesson", + "schema": "public", + "values": [ + "online", + "offline", + "both" + ] + }, + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "learner", + "tutor" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index c1993b4..ada04b8 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -127,6 +127,13 @@ "when": 1733644648448, "tag": "0017_flowery_ego", "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1733656234950, + "tag": "0018_short_ikaris", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index aa273b9..d4184d7 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -144,6 +144,7 @@ export const orders = pgTable( typeLesson: typeLessonEnum("type_lesson").notNull(), status: orderStatusEnum("status"), price: integer("price").notNull(), + reviewDismissedAt: timestamp("review_dismissed_at"), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at"), }, diff --git a/src/module/review/review.controller.ts b/src/module/review/review.controller.ts index e5917dc..9e5fca4 100644 --- a/src/module/review/review.controller.ts +++ b/src/module/review/review.controller.ts @@ -3,7 +3,11 @@ import { Controller } from "@/types"; import { z } from "zod"; import { logger } from "@middleware/logging.middleware"; import { ReviewService } from "./review.service"; -import { createReviewSchema, getReviewsSchema } from "./review.schema"; +import { + createReviewSchema, + dismissReviewSchema, + getReviewsSchema, +} from "./review.schema"; const reviewService = new ReviewService({ reviewRepository: container.reviewRepository, @@ -103,3 +107,41 @@ export const getTutoriesReviews: Controller = async ( }); } }; + +type DismissReviewSchema = z.infer; +export const dismissReviewPrompt: Controller = async ( + req, + res, +) => { + try { + const learnerId = req.learner.id; + const { orderId } = req.params; + + // Validate that the learner owns this order + const isValidOrder = await reviewService.validateOrderOwnership( + orderId, + learnerId, + ); + if (!isValidOrder) { + res.status(403).json({ + status: "fail", + message: + "You can only dismiss review prompt for orders that you've made", + }); + return; + } + + await reviewService.dismissReviewPrompt(orderId); + + res.json({ + status: "success", + message: "Review prompt dismissed successfully", + }); + } catch (error) { + logger.error(`Failed to dismiss review: ${error}`); + res.status(500).json({ + status: "error", + message: "Failed to dismiss review", + }); + } +}; diff --git a/src/module/review/review.repository.ts b/src/module/review/review.repository.ts index 55a9b93..8743f91 100644 --- a/src/module/review/review.repository.ts +++ b/src/module/review/review.repository.ts @@ -1,5 +1,5 @@ import { db as dbType } from "@/db/config"; -import { reviews, orders, learners, tutories } from "@/db/schema"; +import { reviews, orders, learners } from "@/db/schema"; import { desc, eq, inArray, sql } from "drizzle-orm"; export class ReviewRepository { @@ -78,4 +78,11 @@ export class ReviewRepository { return results; } + + async dismissReviewPrompt(orderId: string) { + return this.db + .update(orders) + .set({ reviewDismissedAt: new Date() }) + .where(eq(orders.id, orderId)); + } } diff --git a/src/module/review/review.route.ts b/src/module/review/review.route.ts index b2aa2ac..0bdbd02 100644 --- a/src/module/review/review.route.ts +++ b/src/module/review/review.route.ts @@ -5,7 +5,11 @@ import { import { validator } from "@middleware/validation.middleware"; import { Router } from "express"; import * as reviewController from "./review.controller"; -import { createReviewSchema, getReviewsSchema } from "./review.schema"; +import { + createReviewSchema, + dismissReviewSchema, + getReviewsSchema, +} from "./review.schema"; const reviewRouter = Router(); @@ -31,4 +35,11 @@ reviewRouter.post( reviewController.createReview, ); +reviewRouter.post( + "/orders/:orderId/disimss", + // #swagger.tags = ['reviews'] + validator(dismissReviewSchema), + reviewController.dismissReviewPrompt, +); + export default reviewRouter; diff --git a/src/module/review/review.schema.ts b/src/module/review/review.schema.ts index a30e2ad..6381532 100644 --- a/src/module/review/review.schema.ts +++ b/src/module/review/review.schema.ts @@ -10,6 +10,12 @@ export const createReviewSchema = z.object({ }), }); +export const dismissReviewSchema = z.object({ + params: z.object({ + orderId: z.string().uuid(), + }), +}); + export const getReviewsSchema = z.object({ params: z.object({ tutoriesId: z.string().uuid(), diff --git a/src/module/review/review.service.ts b/src/module/review/review.service.ts index fee0126..c4918af 100644 --- a/src/module/review/review.service.ts +++ b/src/module/review/review.service.ts @@ -56,6 +56,10 @@ export class ReviewService { return orders.some((order) => order.status === "completed"); } + async dismissReviewPrompt(orderId: string) { + return this.deps.reviewRepository.dismissReviewPrompt(orderId); + } + async hasReview(orderId: string) { return this.deps.reviewRepository.hasReview(orderId); }