create review and profile

This commit is contained in:
arifal
2026-04-20 00:25:05 +07:00
parent 7159e9108f
commit ba5f64ae0e
37 changed files with 3324 additions and 109 deletions
+15 -3
View File
@@ -8,6 +8,7 @@ import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { createTripAction } from "@/features/trip/actions";
import { ImageUrlInput } from "@/features/trip/components/image-url-input";
import { formatLocalCalendarYmd } from "@/lib/trip-dates";
const SAMPLE_MOUNTAINS = [
{ name: "Gunung Papandayan", location: "Garut, Jawa Barat" },
@@ -72,10 +73,15 @@ export default function CreateTripPage() {
setLoading(true);
const formData = new FormData(e.currentTarget);
// Set date values from DatePicker state
formData.set("date", startDate.toISOString().split("T")[0]);
// Hari kalender lokal → YYYY-MM-DD (bukan toISOString, supaya tidak geser ke UTC)
formData.set("date", formatLocalCalendarYmd(startDate));
if (endDate) {
formData.set("endDate", endDate.toISOString().split("T")[0]);
const startYmd = formatLocalCalendarYmd(startDate);
const endYmd = formatLocalCalendarYmd(endDate);
// Satu hari: tanggal pulang sama dengan berangkat → jangan kirim endDate (trip 1 hari)
if (endYmd !== startYmd) {
formData.set("endDate", endYmd);
}
}
// Set raw price number
formData.set("price", parseRupiahInput(priceDisplay));
@@ -222,6 +228,12 @@ export default function CreateTripPage() {
<label className="mb-1.5 block text-sm font-semibold text-neutral-700">
Tanggal Berangkat Pulang
</label>
<p className="mb-1.5 text-[11px] leading-snug text-neutral-500 sm:text-xs">
Pilih satu tanggal untuk trip <span className="font-medium">satu hari</span>
. Pilih rentang untuk trip <span className="font-medium">lebih dari satu hari</span>
. Tanggal disimpan sebagai hari kalender yang kamu klik; filter Open Trip memakai{" "}
<span className="font-medium">UTC</span> yang sama.
</p>
<div className="relative">
<span className="absolute left-3 top-1/2 z-10 -translate-y-1/2 text-neutral-400">
<svg
+5
View File
@@ -27,6 +27,11 @@ export type User = Prisma.UserModel
*
*/
export type Trip = Prisma.TripModel
/**
* Model TripReview
*
*/
export type TripReview = Prisma.TripReviewModel
/**
* Model TripImage
*
+5
View File
@@ -51,6 +51,11 @@ export type User = Prisma.UserModel
*
*/
export type Trip = Prisma.TripModel
/**
* Model TripReview
*
*/
export type TripReview = Prisma.TripReviewModel
/**
* Model TripImage
*
File diff suppressed because one or more lines are too long
@@ -386,6 +386,7 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
export const ModelName = {
User: 'User',
Trip: 'Trip',
TripReview: 'TripReview',
TripImage: 'TripImage',
TripParticipant: 'TripParticipant'
} as const
@@ -403,7 +404,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
omit: GlobalOmitOptions
}
meta: {
modelProps: "user" | "trip" | "tripImage" | "tripParticipant"
modelProps: "user" | "trip" | "tripReview" | "tripImage" | "tripParticipant"
txIsolationLevel: TransactionIsolationLevel
}
model: {
@@ -555,6 +556,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
}
}
}
TripReview: {
payload: Prisma.$TripReviewPayload<ExtArgs>
fields: Prisma.TripReviewFieldRefs
operations: {
findUnique: {
args: Prisma.TripReviewFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload> | null
}
findUniqueOrThrow: {
args: Prisma.TripReviewFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>
}
findFirst: {
args: Prisma.TripReviewFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload> | null
}
findFirstOrThrow: {
args: Prisma.TripReviewFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>
}
findMany: {
args: Prisma.TripReviewFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>[]
}
create: {
args: Prisma.TripReviewCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>
}
createMany: {
args: Prisma.TripReviewCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.TripReviewCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>[]
}
delete: {
args: Prisma.TripReviewDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>
}
update: {
args: Prisma.TripReviewUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>
}
deleteMany: {
args: Prisma.TripReviewDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.TripReviewUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.TripReviewUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>[]
}
upsert: {
args: Prisma.TripReviewUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripReviewPayload>
}
aggregate: {
args: Prisma.TripReviewAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateTripReview>
}
groupBy: {
args: Prisma.TripReviewGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.TripReviewGroupByOutputType>[]
}
count: {
args: Prisma.TripReviewCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.TripReviewCountAggregateOutputType> | number
}
}
}
TripImage: {
payload: Prisma.$TripImagePayload<ExtArgs>
fields: Prisma.TripImageFieldRefs
@@ -774,6 +849,19 @@ export const TripScalarFieldEnum = {
export type TripScalarFieldEnum = (typeof TripScalarFieldEnum)[keyof typeof TripScalarFieldEnum]
export const TripReviewScalarFieldEnum = {
id: 'id',
rating: 'rating',
comment: 'comment',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
tripId: 'tripId',
userId: 'userId'
} as const
export type TripReviewScalarFieldEnum = (typeof TripReviewScalarFieldEnum)[keyof typeof TripReviewScalarFieldEnum]
export const TripImageScalarFieldEnum = {
id: 'id',
url: 'url',
@@ -1006,6 +1094,7 @@ export type PrismaClientOptions = ({
export type GlobalOmitConfig = {
user?: Prisma.UserOmit
trip?: Prisma.TripOmit
tripReview?: Prisma.TripReviewOmit
tripImage?: Prisma.TripImageOmit
tripParticipant?: Prisma.TripParticipantOmit
}
@@ -53,6 +53,7 @@ export const AnyNull = runtime.AnyNull
export const ModelName = {
User: 'User',
Trip: 'Trip',
TripReview: 'TripReview',
TripImage: 'TripImage',
TripParticipant: 'TripParticipant'
} as const
@@ -105,6 +106,19 @@ export const TripScalarFieldEnum = {
export type TripScalarFieldEnum = (typeof TripScalarFieldEnum)[keyof typeof TripScalarFieldEnum]
export const TripReviewScalarFieldEnum = {
id: 'id',
rating: 'rating',
comment: 'comment',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
tripId: 'tripId',
userId: 'userId'
} as const
export type TripReviewScalarFieldEnum = (typeof TripReviewScalarFieldEnum)[keyof typeof TripReviewScalarFieldEnum]
export const TripImageScalarFieldEnum = {
id: 'id',
url: 'url',
+1
View File
@@ -10,6 +10,7 @@
*/
export type * from './models/User'
export type * from './models/Trip'
export type * from './models/TripReview'
export type * from './models/TripImage'
export type * from './models/TripParticipant'
export type * from './commonInputTypes'
+158
View File
@@ -287,6 +287,7 @@ export type TripWhereInput = {
organizer?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
participants?: Prisma.TripParticipantListRelationFilter
images?: Prisma.TripImageListRelationFilter
reviews?: Prisma.TripReviewListRelationFilter
}
export type TripOrderByWithRelationInput = {
@@ -306,6 +307,7 @@ export type TripOrderByWithRelationInput = {
organizer?: Prisma.UserOrderByWithRelationInput
participants?: Prisma.TripParticipantOrderByRelationAggregateInput
images?: Prisma.TripImageOrderByRelationAggregateInput
reviews?: Prisma.TripReviewOrderByRelationAggregateInput
}
export type TripWhereUniqueInput = Prisma.AtLeast<{
@@ -328,6 +330,7 @@ export type TripWhereUniqueInput = Prisma.AtLeast<{
organizer?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
participants?: Prisma.TripParticipantListRelationFilter
images?: Prisma.TripImageListRelationFilter
reviews?: Prisma.TripReviewListRelationFilter
}, "id">
export type TripOrderByWithAggregationInput = {
@@ -386,6 +389,7 @@ export type TripCreateInput = {
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewCreateNestedManyWithoutTripInput
}
export type TripUncheckedCreateInput = {
@@ -404,6 +408,7 @@ export type TripUncheckedCreateInput = {
organizerId: string
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutTripInput
}
export type TripUpdateInput = {
@@ -422,6 +427,7 @@ export type TripUpdateInput = {
organizer?: Prisma.UserUpdateOneRequiredWithoutTripsNestedInput
participants?: Prisma.TripParticipantUpdateManyWithoutTripNestedInput
images?: Prisma.TripImageUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUpdateManyWithoutTripNestedInput
}
export type TripUncheckedUpdateInput = {
@@ -440,6 +446,7 @@ export type TripUncheckedUpdateInput = {
organizerId?: Prisma.StringFieldUpdateOperationsInput | string
participants?: Prisma.TripParticipantUncheckedUpdateManyWithoutTripNestedInput
images?: Prisma.TripImageUncheckedUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUncheckedUpdateManyWithoutTripNestedInput
}
export type TripCreateManyInput = {
@@ -620,6 +627,20 @@ export type EnumTripStatusFieldUpdateOperationsInput = {
set?: $Enums.TripStatus
}
export type TripCreateNestedOneWithoutReviewsInput = {
create?: Prisma.XOR<Prisma.TripCreateWithoutReviewsInput, Prisma.TripUncheckedCreateWithoutReviewsInput>
connectOrCreate?: Prisma.TripCreateOrConnectWithoutReviewsInput
connect?: Prisma.TripWhereUniqueInput
}
export type TripUpdateOneRequiredWithoutReviewsNestedInput = {
create?: Prisma.XOR<Prisma.TripCreateWithoutReviewsInput, Prisma.TripUncheckedCreateWithoutReviewsInput>
connectOrCreate?: Prisma.TripCreateOrConnectWithoutReviewsInput
upsert?: Prisma.TripUpsertWithoutReviewsInput
connect?: Prisma.TripWhereUniqueInput
update?: Prisma.XOR<Prisma.XOR<Prisma.TripUpdateToOneWithWhereWithoutReviewsInput, Prisma.TripUpdateWithoutReviewsInput>, Prisma.TripUncheckedUpdateWithoutReviewsInput>
}
export type TripCreateNestedOneWithoutImagesInput = {
create?: Prisma.XOR<Prisma.TripCreateWithoutImagesInput, Prisma.TripUncheckedCreateWithoutImagesInput>
connectOrCreate?: Prisma.TripCreateOrConnectWithoutImagesInput
@@ -663,6 +684,7 @@ export type TripCreateWithoutOrganizerInput = {
updatedAt?: Date | string
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewCreateNestedManyWithoutTripInput
}
export type TripUncheckedCreateWithoutOrganizerInput = {
@@ -680,6 +702,7 @@ export type TripUncheckedCreateWithoutOrganizerInput = {
updatedAt?: Date | string
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutTripInput
}
export type TripCreateOrConnectWithoutOrganizerInput = {
@@ -727,6 +750,94 @@ export type TripScalarWhereInput = {
organizerId?: Prisma.StringFilter<"Trip"> | string
}
export type TripCreateWithoutReviewsInput = {
id?: string
title: string
description?: string | null
mountain: string
location: string
date: Date | string
endDate?: Date | string | null
maxParticipants: number
price: number
status?: $Enums.TripStatus
createdAt?: Date | string
updatedAt?: Date | string
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
}
export type TripUncheckedCreateWithoutReviewsInput = {
id?: string
title: string
description?: string | null
mountain: string
location: string
date: Date | string
endDate?: Date | string | null
maxParticipants: number
price: number
status?: $Enums.TripStatus
createdAt?: Date | string
updatedAt?: Date | string
organizerId: string
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
}
export type TripCreateOrConnectWithoutReviewsInput = {
where: Prisma.TripWhereUniqueInput
create: Prisma.XOR<Prisma.TripCreateWithoutReviewsInput, Prisma.TripUncheckedCreateWithoutReviewsInput>
}
export type TripUpsertWithoutReviewsInput = {
update: Prisma.XOR<Prisma.TripUpdateWithoutReviewsInput, Prisma.TripUncheckedUpdateWithoutReviewsInput>
create: Prisma.XOR<Prisma.TripCreateWithoutReviewsInput, Prisma.TripUncheckedCreateWithoutReviewsInput>
where?: Prisma.TripWhereInput
}
export type TripUpdateToOneWithWhereWithoutReviewsInput = {
where?: Prisma.TripWhereInput
data: Prisma.XOR<Prisma.TripUpdateWithoutReviewsInput, Prisma.TripUncheckedUpdateWithoutReviewsInput>
}
export type TripUpdateWithoutReviewsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
title?: Prisma.StringFieldUpdateOperationsInput | string
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
mountain?: Prisma.StringFieldUpdateOperationsInput | string
location?: Prisma.StringFieldUpdateOperationsInput | string
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
endDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
price?: Prisma.IntFieldUpdateOperationsInput | number
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
organizer?: Prisma.UserUpdateOneRequiredWithoutTripsNestedInput
participants?: Prisma.TripParticipantUpdateManyWithoutTripNestedInput
images?: Prisma.TripImageUpdateManyWithoutTripNestedInput
}
export type TripUncheckedUpdateWithoutReviewsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
title?: Prisma.StringFieldUpdateOperationsInput | string
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
mountain?: Prisma.StringFieldUpdateOperationsInput | string
location?: Prisma.StringFieldUpdateOperationsInput | string
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
endDate?: Prisma.NullableDateTimeFieldUpdateOperationsInput | Date | string | null
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
price?: Prisma.IntFieldUpdateOperationsInput | number
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
organizerId?: Prisma.StringFieldUpdateOperationsInput | string
participants?: Prisma.TripParticipantUncheckedUpdateManyWithoutTripNestedInput
images?: Prisma.TripImageUncheckedUpdateManyWithoutTripNestedInput
}
export type TripCreateWithoutImagesInput = {
id?: string
title: string
@@ -742,6 +853,7 @@ export type TripCreateWithoutImagesInput = {
updatedAt?: Date | string
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewCreateNestedManyWithoutTripInput
}
export type TripUncheckedCreateWithoutImagesInput = {
@@ -759,6 +871,7 @@ export type TripUncheckedCreateWithoutImagesInput = {
updatedAt?: Date | string
organizerId: string
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutTripInput
}
export type TripCreateOrConnectWithoutImagesInput = {
@@ -792,6 +905,7 @@ export type TripUpdateWithoutImagesInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
organizer?: Prisma.UserUpdateOneRequiredWithoutTripsNestedInput
participants?: Prisma.TripParticipantUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUpdateManyWithoutTripNestedInput
}
export type TripUncheckedUpdateWithoutImagesInput = {
@@ -809,6 +923,7 @@ export type TripUncheckedUpdateWithoutImagesInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
organizerId?: Prisma.StringFieldUpdateOperationsInput | string
participants?: Prisma.TripParticipantUncheckedUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUncheckedUpdateManyWithoutTripNestedInput
}
export type TripCreateWithoutParticipantsInput = {
@@ -826,6 +941,7 @@ export type TripCreateWithoutParticipantsInput = {
updatedAt?: Date | string
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewCreateNestedManyWithoutTripInput
}
export type TripUncheckedCreateWithoutParticipantsInput = {
@@ -843,6 +959,7 @@ export type TripUncheckedCreateWithoutParticipantsInput = {
updatedAt?: Date | string
organizerId: string
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
reviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutTripInput
}
export type TripCreateOrConnectWithoutParticipantsInput = {
@@ -876,6 +993,7 @@ export type TripUpdateWithoutParticipantsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
organizer?: Prisma.UserUpdateOneRequiredWithoutTripsNestedInput
images?: Prisma.TripImageUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUpdateManyWithoutTripNestedInput
}
export type TripUncheckedUpdateWithoutParticipantsInput = {
@@ -893,6 +1011,7 @@ export type TripUncheckedUpdateWithoutParticipantsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
organizerId?: Prisma.StringFieldUpdateOperationsInput | string
images?: Prisma.TripImageUncheckedUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUncheckedUpdateManyWithoutTripNestedInput
}
export type TripCreateManyOrganizerInput = {
@@ -925,6 +1044,7 @@ export type TripUpdateWithoutOrganizerInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
participants?: Prisma.TripParticipantUpdateManyWithoutTripNestedInput
images?: Prisma.TripImageUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUpdateManyWithoutTripNestedInput
}
export type TripUncheckedUpdateWithoutOrganizerInput = {
@@ -942,6 +1062,7 @@ export type TripUncheckedUpdateWithoutOrganizerInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
participants?: Prisma.TripParticipantUncheckedUpdateManyWithoutTripNestedInput
images?: Prisma.TripImageUncheckedUpdateManyWithoutTripNestedInput
reviews?: Prisma.TripReviewUncheckedUpdateManyWithoutTripNestedInput
}
export type TripUncheckedUpdateManyWithoutOrganizerInput = {
@@ -967,11 +1088,13 @@ export type TripUncheckedUpdateManyWithoutOrganizerInput = {
export type TripCountOutputType = {
participants: number
images: number
reviews: number
}
export type TripCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
participants?: boolean | TripCountOutputTypeCountParticipantsArgs
images?: boolean | TripCountOutputTypeCountImagesArgs
reviews?: boolean | TripCountOutputTypeCountReviewsArgs
}
/**
@@ -998,6 +1121,13 @@ export type TripCountOutputTypeCountImagesArgs<ExtArgs extends runtime.Types.Ext
where?: Prisma.TripImageWhereInput
}
/**
* TripCountOutputType without action
*/
export type TripCountOutputTypeCountReviewsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
where?: Prisma.TripReviewWhereInput
}
export type TripSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
@@ -1016,6 +1146,7 @@ export type TripSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
organizer?: boolean | Prisma.UserDefaultArgs<ExtArgs>
participants?: boolean | Prisma.Trip$participantsArgs<ExtArgs>
images?: boolean | Prisma.Trip$imagesArgs<ExtArgs>
reviews?: boolean | Prisma.Trip$reviewsArgs<ExtArgs>
_count?: boolean | Prisma.TripCountOutputTypeDefaultArgs<ExtArgs>
}, ExtArgs["result"]["trip"]>
@@ -1074,6 +1205,7 @@ export type TripInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs =
organizer?: boolean | Prisma.UserDefaultArgs<ExtArgs>
participants?: boolean | Prisma.Trip$participantsArgs<ExtArgs>
images?: boolean | Prisma.Trip$imagesArgs<ExtArgs>
reviews?: boolean | Prisma.Trip$reviewsArgs<ExtArgs>
_count?: boolean | Prisma.TripCountOutputTypeDefaultArgs<ExtArgs>
}
export type TripIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
@@ -1089,6 +1221,7 @@ export type $TripPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
organizer: Prisma.$UserPayload<ExtArgs>
participants: Prisma.$TripParticipantPayload<ExtArgs>[]
images: Prisma.$TripImagePayload<ExtArgs>[]
reviews: Prisma.$TripReviewPayload<ExtArgs>[]
}
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
@@ -1501,6 +1634,7 @@ export interface Prisma__TripClient<T, Null = never, ExtArgs extends runtime.Typ
organizer<T extends Prisma.UserDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.UserDefaultArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
participants<T extends Prisma.Trip$participantsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Trip$participantsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripParticipantPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
images<T extends Prisma.Trip$imagesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Trip$imagesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripImagePayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
reviews<T extends Prisma.Trip$reviewsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Trip$reviewsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripReviewPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
@@ -1991,6 +2125,30 @@ export type Trip$imagesArgs<ExtArgs extends runtime.Types.Extensions.InternalArg
distinct?: Prisma.TripImageScalarFieldEnum | Prisma.TripImageScalarFieldEnum[]
}
/**
* Trip.reviews
*/
export type Trip$reviewsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
/**
* Select specific fields to fetch from the TripReview
*/
select?: Prisma.TripReviewSelect<ExtArgs> | null
/**
* Omit specific fields from the TripReview
*/
omit?: Prisma.TripReviewOmit<ExtArgs> | null
/**
* Choose, which related nodes to fetch as well
*/
include?: Prisma.TripReviewInclude<ExtArgs> | null
where?: Prisma.TripReviewWhereInput
orderBy?: Prisma.TripReviewOrderByWithRelationInput | Prisma.TripReviewOrderByWithRelationInput[]
cursor?: Prisma.TripReviewWhereUniqueInput
take?: number
skip?: number
distinct?: Prisma.TripReviewScalarFieldEnum | Prisma.TripReviewScalarFieldEnum[]
}
/**
* Trip without action
*/
File diff suppressed because it is too large Load Diff
+130
View File
@@ -200,6 +200,7 @@ export type UserWhereInput = {
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
trips?: Prisma.TripListRelationFilter
participations?: Prisma.TripParticipantListRelationFilter
tripReviews?: Prisma.TripReviewListRelationFilter
}
export type UserOrderByWithRelationInput = {
@@ -212,6 +213,7 @@ export type UserOrderByWithRelationInput = {
updatedAt?: Prisma.SortOrder
trips?: Prisma.TripOrderByRelationAggregateInput
participations?: Prisma.TripParticipantOrderByRelationAggregateInput
tripReviews?: Prisma.TripReviewOrderByRelationAggregateInput
}
export type UserWhereUniqueInput = Prisma.AtLeast<{
@@ -227,6 +229,7 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
trips?: Prisma.TripListRelationFilter
participations?: Prisma.TripParticipantListRelationFilter
tripReviews?: Prisma.TripReviewListRelationFilter
}, "id" | "email">
export type UserOrderByWithAggregationInput = {
@@ -265,6 +268,7 @@ export type UserCreateInput = {
updatedAt?: Date | string
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
}
export type UserUncheckedCreateInput = {
@@ -277,6 +281,7 @@ export type UserUncheckedCreateInput = {
updatedAt?: Date | string
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
}
export type UserUpdateInput = {
@@ -289,6 +294,7 @@ export type UserUpdateInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
}
export type UserUncheckedUpdateInput = {
@@ -301,6 +307,7 @@ export type UserUncheckedUpdateInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
}
export type UserCreateManyInput = {
@@ -394,6 +401,20 @@ export type UserUpdateOneRequiredWithoutTripsNestedInput = {
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutTripsInput, Prisma.UserUpdateWithoutTripsInput>, Prisma.UserUncheckedUpdateWithoutTripsInput>
}
export type UserCreateNestedOneWithoutTripReviewsInput = {
create?: Prisma.XOR<Prisma.UserCreateWithoutTripReviewsInput, Prisma.UserUncheckedCreateWithoutTripReviewsInput>
connectOrCreate?: Prisma.UserCreateOrConnectWithoutTripReviewsInput
connect?: Prisma.UserWhereUniqueInput
}
export type UserUpdateOneRequiredWithoutTripReviewsNestedInput = {
create?: Prisma.XOR<Prisma.UserCreateWithoutTripReviewsInput, Prisma.UserUncheckedCreateWithoutTripReviewsInput>
connectOrCreate?: Prisma.UserCreateOrConnectWithoutTripReviewsInput
upsert?: Prisma.UserUpsertWithoutTripReviewsInput
connect?: Prisma.UserWhereUniqueInput
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutTripReviewsInput, Prisma.UserUpdateWithoutTripReviewsInput>, Prisma.UserUncheckedUpdateWithoutTripReviewsInput>
}
export type UserCreateNestedOneWithoutParticipationsInput = {
create?: Prisma.XOR<Prisma.UserCreateWithoutParticipationsInput, Prisma.UserUncheckedCreateWithoutParticipationsInput>
connectOrCreate?: Prisma.UserCreateOrConnectWithoutParticipationsInput
@@ -417,6 +438,7 @@ export type UserCreateWithoutTripsInput = {
createdAt?: Date | string
updatedAt?: Date | string
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
}
export type UserUncheckedCreateWithoutTripsInput = {
@@ -428,6 +450,7 @@ export type UserUncheckedCreateWithoutTripsInput = {
createdAt?: Date | string
updatedAt?: Date | string
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
}
export type UserCreateOrConnectWithoutTripsInput = {
@@ -455,6 +478,7 @@ export type UserUpdateWithoutTripsInput = {
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutTripsInput = {
@@ -466,6 +490,71 @@ export type UserUncheckedUpdateWithoutTripsInput = {
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
}
export type UserCreateWithoutTripReviewsInput = {
id?: string
name: string
email: string
password: string
image?: string | null
createdAt?: Date | string
updatedAt?: Date | string
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
participations?: Prisma.TripParticipantCreateNestedManyWithoutUserInput
}
export type UserUncheckedCreateWithoutTripReviewsInput = {
id?: string
name: string
email: string
password: string
image?: string | null
createdAt?: Date | string
updatedAt?: Date | string
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
participations?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutUserInput
}
export type UserCreateOrConnectWithoutTripReviewsInput = {
where: Prisma.UserWhereUniqueInput
create: Prisma.XOR<Prisma.UserCreateWithoutTripReviewsInput, Prisma.UserUncheckedCreateWithoutTripReviewsInput>
}
export type UserUpsertWithoutTripReviewsInput = {
update: Prisma.XOR<Prisma.UserUpdateWithoutTripReviewsInput, Prisma.UserUncheckedUpdateWithoutTripReviewsInput>
create: Prisma.XOR<Prisma.UserCreateWithoutTripReviewsInput, Prisma.UserUncheckedCreateWithoutTripReviewsInput>
where?: Prisma.UserWhereInput
}
export type UserUpdateToOneWithWhereWithoutTripReviewsInput = {
where?: Prisma.UserWhereInput
data: Prisma.XOR<Prisma.UserUpdateWithoutTripReviewsInput, Prisma.UserUncheckedUpdateWithoutTripReviewsInput>
}
export type UserUpdateWithoutTripReviewsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
email?: Prisma.StringFieldUpdateOperationsInput | string
password?: Prisma.StringFieldUpdateOperationsInput | string
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
participations?: Prisma.TripParticipantUpdateManyWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutTripReviewsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
email?: Prisma.StringFieldUpdateOperationsInput | string
password?: Prisma.StringFieldUpdateOperationsInput | string
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
participations?: Prisma.TripParticipantUncheckedUpdateManyWithoutUserNestedInput
}
export type UserCreateWithoutParticipationsInput = {
@@ -477,6 +566,7 @@ export type UserCreateWithoutParticipationsInput = {
createdAt?: Date | string
updatedAt?: Date | string
trips?: Prisma.TripCreateNestedManyWithoutOrganizerInput
tripReviews?: Prisma.TripReviewCreateNestedManyWithoutUserInput
}
export type UserUncheckedCreateWithoutParticipationsInput = {
@@ -488,6 +578,7 @@ export type UserUncheckedCreateWithoutParticipationsInput = {
createdAt?: Date | string
updatedAt?: Date | string
trips?: Prisma.TripUncheckedCreateNestedManyWithoutOrganizerInput
tripReviews?: Prisma.TripReviewUncheckedCreateNestedManyWithoutUserInput
}
export type UserCreateOrConnectWithoutParticipationsInput = {
@@ -515,6 +606,7 @@ export type UserUpdateWithoutParticipationsInput = {
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
trips?: Prisma.TripUpdateManyWithoutOrganizerNestedInput
tripReviews?: Prisma.TripReviewUpdateManyWithoutUserNestedInput
}
export type UserUncheckedUpdateWithoutParticipationsInput = {
@@ -526,6 +618,7 @@ export type UserUncheckedUpdateWithoutParticipationsInput = {
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
trips?: Prisma.TripUncheckedUpdateManyWithoutOrganizerNestedInput
tripReviews?: Prisma.TripReviewUncheckedUpdateManyWithoutUserNestedInput
}
@@ -536,11 +629,13 @@ export type UserUncheckedUpdateWithoutParticipationsInput = {
export type UserCountOutputType = {
trips: number
participations: number
tripReviews: number
}
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
trips?: boolean | UserCountOutputTypeCountTripsArgs
participations?: boolean | UserCountOutputTypeCountParticipationsArgs
tripReviews?: boolean | UserCountOutputTypeCountTripReviewsArgs
}
/**
@@ -567,6 +662,13 @@ export type UserCountOutputTypeCountParticipationsArgs<ExtArgs extends runtime.T
where?: Prisma.TripParticipantWhereInput
}
/**
* UserCountOutputType without action
*/
export type UserCountOutputTypeCountTripReviewsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
where?: Prisma.TripReviewWhereInput
}
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
@@ -578,6 +680,7 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
updatedAt?: boolean
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
}, ExtArgs["result"]["user"]>
@@ -615,6 +718,7 @@ export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = run
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
trips?: boolean | Prisma.User$tripsArgs<ExtArgs>
participations?: boolean | Prisma.User$participationsArgs<ExtArgs>
tripReviews?: boolean | Prisma.User$tripReviewsArgs<ExtArgs>
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
}
export type UserIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {}
@@ -625,6 +729,7 @@ export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
objects: {
trips: Prisma.$TripPayload<ExtArgs>[]
participations: Prisma.$TripParticipantPayload<ExtArgs>[]
tripReviews: Prisma.$TripReviewPayload<ExtArgs>[]
}
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
@@ -1030,6 +1135,7 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
readonly [Symbol.toStringTag]: "PrismaPromise"
trips<T extends Prisma.User$tripsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
participations<T extends Prisma.User$participationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$participationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripParticipantPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
tripReviews<T extends Prisma.User$tripReviewsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$tripReviewsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$TripReviewPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
@@ -1506,6 +1612,30 @@ export type User$participationsArgs<ExtArgs extends runtime.Types.Extensions.Int
distinct?: Prisma.TripParticipantScalarFieldEnum | Prisma.TripParticipantScalarFieldEnum[]
}
/**
* User.tripReviews
*/
export type User$tripReviewsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
/**
* Select specific fields to fetch from the TripReview
*/
select?: Prisma.TripReviewSelect<ExtArgs> | null
/**
* Omit specific fields from the TripReview
*/
omit?: Prisma.TripReviewOmit<ExtArgs> | null
/**
* Choose, which related nodes to fetch as well
*/
include?: Prisma.TripReviewInclude<ExtArgs> | null
where?: Prisma.TripReviewWhereInput
orderBy?: Prisma.TripReviewOrderByWithRelationInput | Prisma.TripReviewOrderByWithRelationInput[]
cursor?: Prisma.TripReviewWhereUniqueInput
take?: number
skip?: number
distinct?: Prisma.TripReviewScalarFieldEnum | Prisma.TripReviewScalarFieldEnum[]
}
/**
* User without action
*/
+19 -4
View File
@@ -1,13 +1,19 @@
"use client";
import { useState } from "react";
import { useState, Suspense } from "react";
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
export default function LoginPage() {
function safeInternalPath(raw: string | null): string {
if (!raw || !raw.startsWith("/") || raw.startsWith("//")) return "/";
return raw;
}
function LoginForm() {
const router = useRouter();
const searchParams = useSearchParams();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
@@ -31,7 +37,8 @@ export default function LoginPage() {
if (result?.error) {
setError(result.error);
} else {
router.push("/");
const next = safeInternalPath(searchParams.get("callbackUrl"));
router.push(next);
router.refresh();
}
}
@@ -126,3 +133,11 @@ export default function LoginPage() {
</div>
);
}
export default function LoginPage() {
return (
<Suspense fallback={null}>
<LoginForm />
</Suspense>
);
}
+217
View File
@@ -0,0 +1,217 @@
import { redirect } from "next/navigation";
import Image from "next/image";
import Link from "next/link";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { profileService } from "@/server/services/profile.service";
import { TripCard } from "@/features/trip/components/trip-card";
import { ProfileTripRow } from "@/features/profile/components/profile-trip-row";
export default async function ProfilePage() {
const session = await getServerSession(authOptions);
if (!session?.user) {
redirect("/login?callbackUrl=/profile");
}
const data = await profileService.getProfileDashboard(session.user.id);
const { user, organizedTrips, activeJoined, cancelledJoined, reviewable } =
data;
const memberSince = new Intl.DateTimeFormat("id-ID", {
month: "long",
year: "numeric",
}).format(user.createdAt);
return (
<div className="mx-auto max-w-4xl px-4 py-6 sm:py-8">
<div className="mb-6 flex flex-col gap-4 rounded-2xl border border-neutral-200 bg-white p-5 shadow-sm sm:flex-row sm:items-center sm:justify-between sm:p-6">
<div className="flex items-center gap-4">
{user.image ? (
<Image
src={user.image}
alt=""
width={72}
height={72}
className="h-[72px] w-[72px] rounded-full object-cover"
/>
) : (
<div className="flex h-[72px] w-[72px] shrink-0 items-center justify-center rounded-full bg-primary-600 text-2xl font-bold text-white">
{user.name.charAt(0).toUpperCase()}
</div>
)}
<div className="min-w-0">
<h1 className="text-xl font-bold text-neutral-800 sm:text-2xl">
{user.name}
</h1>
<p className="mt-0.5 truncate text-sm text-neutral-500">{user.email}</p>
<p className="mt-1 text-xs text-neutral-400">
Anggota sejak {memberSince}
</p>
<p className="mt-2 text-xs text-neutral-500">
Di sini kamu bisa lihat trip yang kamu buat sebagai{" "}
<span className="font-semibold text-primary-700">organizer</span>,
trip yang kamu{" "}
<span className="font-semibold text-secondary-700">ikuti</span>{" "}
sebagai peserta, dan{" "}
<span className="font-semibold text-amber-700">ulasan</span> untuk
trip yang sudah selesai (lewat halaman trip).
</p>
</div>
</div>
<Link
href="/create-trip"
className="shrink-0 rounded-xl bg-primary-600 px-4 py-2.5 text-center text-sm font-semibold text-white shadow-md hover:bg-primary-700"
>
+ Buat trip
</Link>
</div>
{/* Trip selesai — akses ulasan (trip ini tidak muncul di Open Trip) */}
{reviewable.length > 0 && (
<section className="mb-8 rounded-2xl border border-amber-200 bg-amber-50/60 p-4 sm:p-5">
<h2 className="mb-1 text-base font-bold text-amber-900 sm:text-lg">
Trip selesai & ulasan
</h2>
<p className="mb-4 text-xs text-amber-900/80 sm:text-sm">
Trip yang sudah lewat tidak tampil di daftar Open Trip. Buka trip di
bawah ini lalu scroll ke bagian ulasan di halaman detail untuk
memberi atau mengubah rating.
</p>
<ul className="space-y-2">
{reviewable.map((p) => {
const t = p.trip;
const hasReview = t.reviews.length > 0;
return (
<li key={p.id}>
<ProfileTripRow
href={`/trips/${t.id}`}
title={t.title}
mountain={t.mountain}
date={t.date}
endDate={t.endDate}
rightSlot={
<span
className={
hasReview
? "text-secondary-700"
: "font-bold text-amber-800"
}
>
{hasReview ? "Ubah ulasan →" : "Beri ulasan →"}
</span>
}
/>
</li>
);
})}
</ul>
</section>
)}
<div className="grid gap-8 lg:grid-cols-2">
<section>
<h2 className="mb-3 text-base font-bold text-neutral-800 sm:text-lg">
Trip yang kamu buat
<span className="ml-2 text-sm font-normal text-neutral-400">
({organizedTrips.length})
</span>
</h2>
{organizedTrips.length === 0 ? (
<p className="rounded-xl border border-dashed border-neutral-200 bg-neutral-50 px-4 py-6 text-center text-sm text-neutral-500">
Belum ada trip.{" "}
<Link href="/create-trip" className="font-semibold text-primary-600">
Buat trip pertama
</Link>
</p>
) : (
<div className="space-y-4">
{organizedTrips.map((trip) => (
<TripCard
key={trip.id}
id={trip.id}
title={trip.title}
mountain={trip.mountain}
location={trip.location}
date={trip.date}
endDate={trip.endDate}
price={trip.price}
maxParticipants={trip.maxParticipants}
participantCount={trip._count.participants}
organizerName={`${user.name} (Kamu)`}
status={trip.status}
coverImage={trip.images[0]?.url}
/>
))}
</div>
)}
</section>
<section>
<h2 className="mb-3 text-base font-bold text-neutral-800 sm:text-lg">
Trip yang kamu ikuti
<span className="ml-2 text-sm font-normal text-neutral-400">
({activeJoined.length})
</span>
</h2>
{activeJoined.length === 0 ? (
<p className="rounded-xl border border-dashed border-neutral-200 bg-neutral-50 px-4 py-6 text-center text-sm text-neutral-500">
Belum join trip.{" "}
<Link href="/trips" className="font-semibold text-primary-600">
Cari open trip
</Link>
</p>
) : (
<ul className="space-y-2">
{activeJoined.map((p) => {
const t = p.trip;
return (
<li key={p.id}>
<ProfileTripRow
href={`/trips/${t.id}`}
title={t.title}
mountain={t.mountain}
date={t.date}
endDate={t.endDate}
rightSlot={
<span className="text-neutral-400">
{p.status === "CONFIRMED" ? "Terdaftar" : p.status}
</span>
}
/>
</li>
);
})}
</ul>
)}
{cancelledJoined.length > 0 && (
<div className="mt-6">
<h3 className="mb-2 text-sm font-semibold text-neutral-500">
Riwayat batal ({cancelledJoined.length})
</h3>
<ul className="space-y-2 opacity-80">
{cancelledJoined.map((p) => {
const t = p.trip;
return (
<li key={p.id}>
<ProfileTripRow
href={`/trips/${t.id}`}
title={t.title}
mountain={t.mountain}
date={t.date}
endDate={t.endDate}
rightSlot={
<span className="text-red-500/90">Dibatalkan</span>
}
/>
</li>
);
})}
</ul>
</div>
)}
</section>
</div>
</div>
);
}
+44
View File
@@ -6,6 +6,11 @@ import { tripService } from "@/server/services/trip.service";
import { formatRupiah, formatDateRange } from "@/lib/utils";
import { JoinTripButton } from "@/features/trip/components/join-trip-button";
import { ImageGallery } from "@/features/trip/components/image-gallery";
import { TripReviewSection } from "@/features/review/components/trip-review-section";
import {
isPastTripLastDayForReview,
isTripDepartureDayPast,
} from "@/lib/trip-dates";
export default async function TripDetailPage({
params,
@@ -38,6 +43,26 @@ export default async function TripDetailPage({
)
: null;
const isDeparturePast = isTripDepartureDayPast(trip.date);
const canReview =
!!session?.user &&
!isOrganizer &&
currentParticipation?.status === "CONFIRMED" &&
isPastTripLastDayForReview(trip.date, trip.endDate);
const myReview = session?.user
? trip.reviews.find((r) => r.userId === session.user.id) ?? null
: null;
const averageRating =
trip.reviews.length > 0
? Math.round(
(trip.reviews.reduce((s, r) => s + r.rating, 0) /
trip.reviews.length) *
10
) / 10
: null;
return (
<div className="mx-auto max-w-3xl px-4 py-4 sm:py-8">
{/* Breadcrumb */}
@@ -180,6 +205,25 @@ export default async function TripDetailPage({
isJoined={!!currentParticipation}
isFull={spotsLeft <= 0}
tripStatus={trip.status}
isDeparturePast={isDeparturePast}
/>
<TripReviewSection
tripId={trip.id}
reviews={trip.reviews.map((r) => ({
id: r.id,
rating: r.rating,
comment: r.comment,
createdAt: r.createdAt,
user: r.user,
}))}
averageRating={averageRating}
canReview={canReview}
myReview={
myReview
? { rating: myReview.rating, comment: myReview.comment }
: null
}
/>
{/* Participants List */}