Skip to content
This repository has been archived by the owner on Jan 18, 2025. It is now read-only.

Commit

Permalink
Merge pull request #36 from SvelteShipSolutions/fix/review
Browse files Browse the repository at this point in the history
Fix: Review of Delivery
  • Loading branch information
Verdone authored Nov 28, 2024
2 parents 5fa8df7 + 5765eb1 commit bad822f
Show file tree
Hide file tree
Showing 15 changed files with 1,316 additions and 730 deletions.
661 changes: 661 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"stripe-event-types": "^3.1.0",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"sveltekit-superforms": "^2.20.1",
"tailwindcss": "^3.3.6",
"ts-node": "^10.9.2",
"tslib": "^2.4.1",
Expand All @@ -70,8 +71,8 @@
"@sveltejs/adapter-vercel": "^4.0.5",
"@threlte/core": "^7.3.1",
"@types/leaflet": "^1.9.14",
"@types/three": "^0.170.0",
"@types/leaflet-routing-machine": "^3.2.8",
"@types/three": "^0.170.0",
"@vercel/analytics": "^1.1.1",
"@vercel/blob": "^0.22.1",
"@vercel/speed-insights": "^1.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- CreateTable
CREATE TABLE "Review" (
"id" SERIAL NOT NULL,
"rating" INTEGER NOT NULL,
"deliveryRating" INTEGER NOT NULL,
"wasDeliveryOnTime" BOOLEAN NOT NULL,
"comment" TEXT,
"userId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"transactionId" INTEGER NOT NULL,

CONSTRAINT "Review_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Review_transactionId_key" ON "Review"("transactionId");

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_transactionId_fkey" FOREIGN KEY ("transactionId") REFERENCES "ShipmentTransaction"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
28 changes: 11 additions & 17 deletions prisma/schema/Review.prisma
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
model Review {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
rating Int
deliveryRating Int
wasDeliveryOnTime Boolean
comment String?
userId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
images Image[]
}

model Image {
id Int @id @default(autoincrement())
url String
reviewId Int
review Review @relation(fields: [reviewId], references: [id])
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
rating Int
deliveryRating Int
wasDeliveryOnTime Boolean
comment String?
userId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
shipmentTransaction ShipmentTransaction @relation(fields: [transactionId], references: [id])
transactionId Int @unique
}
2 changes: 2 additions & 0 deletions prisma/schema/ShipmentTransaction.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ model ShipmentTransaction {
shipperId Int
trackingEvents TrackingEvent[]
Review Review?
}
33 changes: 33 additions & 0 deletions src/lib/domain/Review.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export class Review {
id?: number;
createdAt?: Date;
updatedAt?: Date;
userId: number;
rating: number;
deliveryRating: number;
wasDeliveryOnTime: boolean;
comment?: string;
transactionId: number;

constructor(params: {
id?: number;
createdAt?: Date;
updatedAt?: Date;
userId: number;
rating: number;
deliveryRating: number;
wasDeliveryOnTime: boolean;
comment?: string | null;
transactionId: number;
}) {
this.id = params.id;
this.createdAt = params.createdAt;
this.updatedAt = params.updatedAt;
this.userId = params.userId;
this.rating = params.rating;
this.deliveryRating = params.deliveryRating;
this.wasDeliveryOnTime = params.wasDeliveryOnTime;
this.comment = params.comment ?? undefined;
this.transactionId = params.transactionId;
}
}
21 changes: 21 additions & 0 deletions src/lib/domain/ReviewFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Review } from "./Review";

export class ReviewFactory {
static create(params: {
userId: number;
rating: number;
deliveryRating: number;
wasDeliveryOnTime: boolean;
comment?: string;
transactionId: number;
}): Review {
return new Review({
userId: params.userId,
rating: params.rating,
deliveryRating: params.deliveryRating,
wasDeliveryOnTime: params.wasDeliveryOnTime,
comment: params.comment ?? undefined,
transactionId: params.transactionId,
});
}
}
92 changes: 92 additions & 0 deletions src/lib/domain/ReviewRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { prisma } from "$lib/db/client";
import { Review } from "./Review";

export class ReviewRepository {
static async save(review: Review): Promise<Review> {
const dataFields = {
userId: review.userId,
rating: review.rating,
deliveryRating: review.deliveryRating,
wasDeliveryOnTime: review.wasDeliveryOnTime,
comment: review.comment ?? undefined, // Normalize null to undefined
transactionId: review.transactionId,
};

const savedReview = await prisma.review.upsert({
where: { transactionId: review.transactionId },
update: dataFields,
create: dataFields,
});

review.id = savedReview.id;
review.createdAt = savedReview.createdAt;
review.updatedAt = savedReview.updatedAt;

return review;
}

static async findById(id: number): Promise<Review | null> {
const dbResult = await prisma.review.findUnique({
where: { id },
});

if (dbResult) {
return new Review({
id: dbResult.id,
createdAt: dbResult.createdAt,
updatedAt: dbResult.updatedAt,
userId: dbResult.userId,
rating: dbResult.rating,
deliveryRating: dbResult.deliveryRating,
wasDeliveryOnTime: dbResult.wasDeliveryOnTime,
comment: dbResult.comment,
transactionId: dbResult.transactionId,
});
}
return null;
}

static async findByTransactionId(
transactionId: number,
): Promise<Review | null> {
const dbResult = await prisma.review.findUnique({
where: { transactionId },
});

if (dbResult) {
return new Review({
id: dbResult.id,
createdAt: dbResult.createdAt,
updatedAt: dbResult.updatedAt,
userId: dbResult.userId,
rating: dbResult.rating,
deliveryRating: dbResult.deliveryRating,
wasDeliveryOnTime: dbResult.wasDeliveryOnTime,
comment: dbResult.comment,
transactionId: dbResult.transactionId,
});
}
return null;
}

static async findByUserId(userId: number): Promise<Review[]> {
const dbResults = await prisma.review.findMany({
where: { userId },
});

return dbResults.map(
(dbResult) =>
new Review({
id: dbResult.id,
createdAt: dbResult.createdAt,
updatedAt: dbResult.updatedAt,
userId: dbResult.userId,
rating: dbResult.rating,
deliveryRating: dbResult.deliveryRating,
wasDeliveryOnTime: dbResult.wasDeliveryOnTime,
comment: dbResult.comment,
transactionId: dbResult.transactionId,
}),
);
}
}
49 changes: 45 additions & 4 deletions src/lib/domain/ShipmentTransactionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export class ShipmentTransactionRepository {
static async findByTrackingNumber(
trackingNumber: string,
): Promise<ShipmentTransaction | null> {
// Piggyback off object-build in find by id
const dbResult = await prisma.shipmentTransaction.findUnique({
select: { id: true },
where: { trackingNumber: trackingNumber },
Expand All @@ -95,6 +94,51 @@ export class ShipmentTransactionRepository {
return [];
}

static async findDeliveredTransactionsWithoutReview(userId: number) {
const deliveredTransactions = await prisma.shipmentTransaction.findMany({
where: {
shipperId: userId, // Filter by the shipper (user) ID
trackingEvents: { some: { type: "DELIVERED" } }, // Must have a "DELIVERED" event
Review: null, // No associated review exists
},
include: { trackingEvents: true },
});

return deliveredTransactions
.filter(
(transaction) =>
transaction.trackingEvents.sort(
(a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
)[0]?.type === "DELIVERED", // Verify that the last tracking event is indeed "DELIVERED"
)
.map((t) => ({
shipmentTransactionId: t.id,
trackingNumber: t.trackingNumber,
}));
}

static async validateTransactionForReview(
transactionId: number,
userId: number,
) {
const transaction = await prisma.shipmentTransaction.findUnique({
where: { id: transactionId },
include: { trackingEvents: true },
});

if (
!transaction ||
transaction.shipperId !== userId || // Verify that it belongs to the user
transaction.trackingEvents.sort(
(a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
)[0]?.type !== "DELIVERED" // Verify that last event is "DELIVERED"
) {
return false;
}

return true;
}

static async getTotalPackages(daysBack: number): Promise<number> {
const count = await prisma.shipmentTransaction.count({
where: {
Expand All @@ -107,7 +151,6 @@ export class ShipmentTransactionRepository {
}

static async getTotalRevenue(daysBack: number): Promise<number> {
// There's no way to do a .aggregate() across a relational boundary
let sum = 0;
const transactions = await prisma.shipmentTransaction.findMany({
select: {
Expand Down Expand Up @@ -148,7 +191,6 @@ export class ShipmentTransactionRepository {
return packageObjects;
}

// Used for the following methods
static getDatesSince(daysBack: number): Date[] {
const endDate = new Date();
const startDate = new Date(
Expand Down Expand Up @@ -194,7 +236,6 @@ export class ShipmentTransactionRepository {
const returnData: { date: string; revenue: number }[] = [];

for (const date of dateList) {
// There's no way to do a .aggregate() across a relational boundary
let sum = 0;
const transactions = await prisma.shipmentTransaction.findMany({
select: {
Expand Down
1 change: 1 addition & 0 deletions src/lib/server/stripeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const stripeSecretKey = process.env.STRIPE_SECRET_KEY;

export const stripe = stripeSecretKey
? new Stripe(stripeSecretKey, {
//@ts-expect-error Type expects latest version "2024-11-20.acacia" instead of current "2024-10-28.acacia"
apiVersion: "2024-10-28.acacia",
})
: null;
Expand Down
40 changes: 0 additions & 40 deletions src/routes/api/review/+server.ts

This file was deleted.

Loading

0 comments on commit bad822f

Please sign in to comment.