add trip image
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
import { createTripAction } from "@/features/trip/actions";
|
||||
import { ImageUrlInput } from "@/features/trip/components/image-url-input";
|
||||
|
||||
const SAMPLE_MOUNTAINS = [
|
||||
{ name: "Gunung Papandayan", location: "Garut, Jawa Barat" },
|
||||
@@ -172,6 +173,8 @@ export default function CreateTripPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ImageUrlInput />
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<div>
|
||||
<label htmlFor="date" className="mb-1.5 block text-sm font-semibold text-neutral-700">
|
||||
|
||||
@@ -27,6 +27,11 @@ export type User = Prisma.UserModel
|
||||
*
|
||||
*/
|
||||
export type Trip = Prisma.TripModel
|
||||
/**
|
||||
* Model TripImage
|
||||
*
|
||||
*/
|
||||
export type TripImage = Prisma.TripImageModel
|
||||
/**
|
||||
* Model TripParticipant
|
||||
*
|
||||
|
||||
@@ -51,6 +51,11 @@ export type User = Prisma.UserModel
|
||||
*
|
||||
*/
|
||||
export type Trip = Prisma.TripModel
|
||||
/**
|
||||
* Model TripImage
|
||||
*
|
||||
*/
|
||||
export type TripImage = Prisma.TripImageModel
|
||||
/**
|
||||
* Model TripParticipant
|
||||
*
|
||||
|
||||
@@ -386,6 +386,7 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
|
||||
export const ModelName = {
|
||||
User: 'User',
|
||||
Trip: 'Trip',
|
||||
TripImage: 'TripImage',
|
||||
TripParticipant: 'TripParticipant'
|
||||
} as const
|
||||
|
||||
@@ -402,7 +403,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
||||
omit: GlobalOmitOptions
|
||||
}
|
||||
meta: {
|
||||
modelProps: "user" | "trip" | "tripParticipant"
|
||||
modelProps: "user" | "trip" | "tripImage" | "tripParticipant"
|
||||
txIsolationLevel: TransactionIsolationLevel
|
||||
}
|
||||
model: {
|
||||
@@ -554,6 +555,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
||||
}
|
||||
}
|
||||
}
|
||||
TripImage: {
|
||||
payload: Prisma.$TripImagePayload<ExtArgs>
|
||||
fields: Prisma.TripImageFieldRefs
|
||||
operations: {
|
||||
findUnique: {
|
||||
args: Prisma.TripImageFindUniqueArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload> | null
|
||||
}
|
||||
findUniqueOrThrow: {
|
||||
args: Prisma.TripImageFindUniqueOrThrowArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>
|
||||
}
|
||||
findFirst: {
|
||||
args: Prisma.TripImageFindFirstArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload> | null
|
||||
}
|
||||
findFirstOrThrow: {
|
||||
args: Prisma.TripImageFindFirstOrThrowArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>
|
||||
}
|
||||
findMany: {
|
||||
args: Prisma.TripImageFindManyArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>[]
|
||||
}
|
||||
create: {
|
||||
args: Prisma.TripImageCreateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>
|
||||
}
|
||||
createMany: {
|
||||
args: Prisma.TripImageCreateManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
createManyAndReturn: {
|
||||
args: Prisma.TripImageCreateManyAndReturnArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>[]
|
||||
}
|
||||
delete: {
|
||||
args: Prisma.TripImageDeleteArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>
|
||||
}
|
||||
update: {
|
||||
args: Prisma.TripImageUpdateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>
|
||||
}
|
||||
deleteMany: {
|
||||
args: Prisma.TripImageDeleteManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
updateMany: {
|
||||
args: Prisma.TripImageUpdateManyArgs<ExtArgs>
|
||||
result: BatchPayload
|
||||
}
|
||||
updateManyAndReturn: {
|
||||
args: Prisma.TripImageUpdateManyAndReturnArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>[]
|
||||
}
|
||||
upsert: {
|
||||
args: Prisma.TripImageUpsertArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$TripImagePayload>
|
||||
}
|
||||
aggregate: {
|
||||
args: Prisma.TripImageAggregateArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.AggregateTripImage>
|
||||
}
|
||||
groupBy: {
|
||||
args: Prisma.TripImageGroupByArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.TripImageGroupByOutputType>[]
|
||||
}
|
||||
count: {
|
||||
args: Prisma.TripImageCountArgs<ExtArgs>
|
||||
result: runtime.Types.Utils.Optional<Prisma.TripImageCountAggregateOutputType> | number
|
||||
}
|
||||
}
|
||||
}
|
||||
TripParticipant: {
|
||||
payload: Prisma.$TripParticipantPayload<ExtArgs>
|
||||
fields: Prisma.TripParticipantFieldRefs
|
||||
@@ -689,7 +764,6 @@ export const TripScalarFieldEnum = {
|
||||
date: 'date',
|
||||
maxParticipants: 'maxParticipants',
|
||||
price: 'price',
|
||||
image: 'image',
|
||||
status: 'status',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
@@ -699,6 +773,17 @@ export const TripScalarFieldEnum = {
|
||||
export type TripScalarFieldEnum = (typeof TripScalarFieldEnum)[keyof typeof TripScalarFieldEnum]
|
||||
|
||||
|
||||
export const TripImageScalarFieldEnum = {
|
||||
id: 'id',
|
||||
url: 'url',
|
||||
caption: 'caption',
|
||||
order: 'order',
|
||||
tripId: 'tripId'
|
||||
} as const
|
||||
|
||||
export type TripImageScalarFieldEnum = (typeof TripImageScalarFieldEnum)[keyof typeof TripImageScalarFieldEnum]
|
||||
|
||||
|
||||
export const TripParticipantScalarFieldEnum = {
|
||||
id: 'id',
|
||||
status: 'status',
|
||||
@@ -920,6 +1005,7 @@ export type PrismaClientOptions = ({
|
||||
export type GlobalOmitConfig = {
|
||||
user?: Prisma.UserOmit
|
||||
trip?: Prisma.TripOmit
|
||||
tripImage?: Prisma.TripImageOmit
|
||||
tripParticipant?: Prisma.TripParticipantOmit
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ export const AnyNull = runtime.AnyNull
|
||||
export const ModelName = {
|
||||
User: 'User',
|
||||
Trip: 'Trip',
|
||||
TripImage: 'TripImage',
|
||||
TripParticipant: 'TripParticipant'
|
||||
} as const
|
||||
|
||||
@@ -94,7 +95,6 @@ export const TripScalarFieldEnum = {
|
||||
date: 'date',
|
||||
maxParticipants: 'maxParticipants',
|
||||
price: 'price',
|
||||
image: 'image',
|
||||
status: 'status',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
@@ -104,6 +104,17 @@ export const TripScalarFieldEnum = {
|
||||
export type TripScalarFieldEnum = (typeof TripScalarFieldEnum)[keyof typeof TripScalarFieldEnum]
|
||||
|
||||
|
||||
export const TripImageScalarFieldEnum = {
|
||||
id: 'id',
|
||||
url: 'url',
|
||||
caption: 'caption',
|
||||
order: 'order',
|
||||
tripId: 'tripId'
|
||||
} as const
|
||||
|
||||
export type TripImageScalarFieldEnum = (typeof TripImageScalarFieldEnum)[keyof typeof TripImageScalarFieldEnum]
|
||||
|
||||
|
||||
export const TripParticipantScalarFieldEnum = {
|
||||
id: 'id',
|
||||
status: 'status',
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
*/
|
||||
export type * from './models/User'
|
||||
export type * from './models/Trip'
|
||||
export type * from './models/TripImage'
|
||||
export type * from './models/TripParticipant'
|
||||
export type * from './commonInputTypes'
|
||||
@@ -45,7 +45,6 @@ export type TripMinAggregateOutputType = {
|
||||
date: Date | null
|
||||
maxParticipants: number | null
|
||||
price: number | null
|
||||
image: string | null
|
||||
status: $Enums.TripStatus | null
|
||||
createdAt: Date | null
|
||||
updatedAt: Date | null
|
||||
@@ -61,7 +60,6 @@ export type TripMaxAggregateOutputType = {
|
||||
date: Date | null
|
||||
maxParticipants: number | null
|
||||
price: number | null
|
||||
image: string | null
|
||||
status: $Enums.TripStatus | null
|
||||
createdAt: Date | null
|
||||
updatedAt: Date | null
|
||||
@@ -77,7 +75,6 @@ export type TripCountAggregateOutputType = {
|
||||
date: number
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image: number
|
||||
status: number
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
@@ -105,7 +102,6 @@ export type TripMinAggregateInputType = {
|
||||
date?: true
|
||||
maxParticipants?: true
|
||||
price?: true
|
||||
image?: true
|
||||
status?: true
|
||||
createdAt?: true
|
||||
updatedAt?: true
|
||||
@@ -121,7 +117,6 @@ export type TripMaxAggregateInputType = {
|
||||
date?: true
|
||||
maxParticipants?: true
|
||||
price?: true
|
||||
image?: true
|
||||
status?: true
|
||||
createdAt?: true
|
||||
updatedAt?: true
|
||||
@@ -137,7 +132,6 @@ export type TripCountAggregateInputType = {
|
||||
date?: true
|
||||
maxParticipants?: true
|
||||
price?: true
|
||||
image?: true
|
||||
status?: true
|
||||
createdAt?: true
|
||||
updatedAt?: true
|
||||
@@ -240,7 +234,6 @@ export type TripGroupByOutputType = {
|
||||
date: Date
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image: string | null
|
||||
status: $Enums.TripStatus
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
@@ -279,13 +272,13 @@ export type TripWhereInput = {
|
||||
date?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
maxParticipants?: Prisma.IntFilter<"Trip"> | number
|
||||
price?: Prisma.IntFilter<"Trip"> | number
|
||||
image?: Prisma.StringNullableFilter<"Trip"> | string | null
|
||||
status?: Prisma.EnumTripStatusFilter<"Trip"> | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
organizerId?: Prisma.StringFilter<"Trip"> | string
|
||||
organizer?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||
participants?: Prisma.TripParticipantListRelationFilter
|
||||
images?: Prisma.TripImageListRelationFilter
|
||||
}
|
||||
|
||||
export type TripOrderByWithRelationInput = {
|
||||
@@ -297,13 +290,13 @@ export type TripOrderByWithRelationInput = {
|
||||
date?: Prisma.SortOrder
|
||||
maxParticipants?: Prisma.SortOrder
|
||||
price?: Prisma.SortOrder
|
||||
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
status?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
organizerId?: Prisma.SortOrder
|
||||
organizer?: Prisma.UserOrderByWithRelationInput
|
||||
participants?: Prisma.TripParticipantOrderByRelationAggregateInput
|
||||
images?: Prisma.TripImageOrderByRelationAggregateInput
|
||||
}
|
||||
|
||||
export type TripWhereUniqueInput = Prisma.AtLeast<{
|
||||
@@ -318,13 +311,13 @@ export type TripWhereUniqueInput = Prisma.AtLeast<{
|
||||
date?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
maxParticipants?: Prisma.IntFilter<"Trip"> | number
|
||||
price?: Prisma.IntFilter<"Trip"> | number
|
||||
image?: Prisma.StringNullableFilter<"Trip"> | string | null
|
||||
status?: Prisma.EnumTripStatusFilter<"Trip"> | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
organizerId?: Prisma.StringFilter<"Trip"> | string
|
||||
organizer?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||
participants?: Prisma.TripParticipantListRelationFilter
|
||||
images?: Prisma.TripImageListRelationFilter
|
||||
}, "id">
|
||||
|
||||
export type TripOrderByWithAggregationInput = {
|
||||
@@ -336,7 +329,6 @@ export type TripOrderByWithAggregationInput = {
|
||||
date?: Prisma.SortOrder
|
||||
maxParticipants?: Prisma.SortOrder
|
||||
price?: Prisma.SortOrder
|
||||
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||
status?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
@@ -360,7 +352,6 @@ export type TripScalarWhereWithAggregatesInput = {
|
||||
date?: Prisma.DateTimeWithAggregatesFilter<"Trip"> | Date | string
|
||||
maxParticipants?: Prisma.IntWithAggregatesFilter<"Trip"> | number
|
||||
price?: Prisma.IntWithAggregatesFilter<"Trip"> | number
|
||||
image?: Prisma.StringNullableWithAggregatesFilter<"Trip"> | string | null
|
||||
status?: Prisma.EnumTripStatusWithAggregatesFilter<"Trip"> | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"Trip"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"Trip"> | Date | string
|
||||
@@ -376,12 +367,12 @@ export type TripCreateInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
|
||||
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
|
||||
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripUncheckedCreateInput = {
|
||||
@@ -393,12 +384,12 @@ export type TripUncheckedCreateInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
organizerId: string
|
||||
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
|
||||
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripUpdateInput = {
|
||||
@@ -410,12 +401,12 @@ export type TripUpdateInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
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 TripUncheckedUpdateInput = {
|
||||
@@ -427,12 +418,12 @@ export type TripUncheckedUpdateInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
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 TripCreateManyInput = {
|
||||
@@ -444,7 +435,6 @@ export type TripCreateManyInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
@@ -460,7 +450,6 @@ export type TripUpdateManyMutationInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
@@ -475,7 +464,6 @@ export type TripUncheckedUpdateManyInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
@@ -501,7 +489,6 @@ export type TripCountOrderByAggregateInput = {
|
||||
date?: Prisma.SortOrder
|
||||
maxParticipants?: Prisma.SortOrder
|
||||
price?: Prisma.SortOrder
|
||||
image?: Prisma.SortOrder
|
||||
status?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
@@ -522,7 +509,6 @@ export type TripMaxOrderByAggregateInput = {
|
||||
date?: Prisma.SortOrder
|
||||
maxParticipants?: Prisma.SortOrder
|
||||
price?: Prisma.SortOrder
|
||||
image?: Prisma.SortOrder
|
||||
status?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
@@ -538,7 +524,6 @@ export type TripMinOrderByAggregateInput = {
|
||||
date?: Prisma.SortOrder
|
||||
maxParticipants?: Prisma.SortOrder
|
||||
price?: Prisma.SortOrder
|
||||
image?: Prisma.SortOrder
|
||||
status?: Prisma.SortOrder
|
||||
createdAt?: Prisma.SortOrder
|
||||
updatedAt?: Prisma.SortOrder
|
||||
@@ -609,6 +594,20 @@ export type EnumTripStatusFieldUpdateOperationsInput = {
|
||||
set?: $Enums.TripStatus
|
||||
}
|
||||
|
||||
export type TripCreateNestedOneWithoutImagesInput = {
|
||||
create?: Prisma.XOR<Prisma.TripCreateWithoutImagesInput, Prisma.TripUncheckedCreateWithoutImagesInput>
|
||||
connectOrCreate?: Prisma.TripCreateOrConnectWithoutImagesInput
|
||||
connect?: Prisma.TripWhereUniqueInput
|
||||
}
|
||||
|
||||
export type TripUpdateOneRequiredWithoutImagesNestedInput = {
|
||||
create?: Prisma.XOR<Prisma.TripCreateWithoutImagesInput, Prisma.TripUncheckedCreateWithoutImagesInput>
|
||||
connectOrCreate?: Prisma.TripCreateOrConnectWithoutImagesInput
|
||||
upsert?: Prisma.TripUpsertWithoutImagesInput
|
||||
connect?: Prisma.TripWhereUniqueInput
|
||||
update?: Prisma.XOR<Prisma.XOR<Prisma.TripUpdateToOneWithWhereWithoutImagesInput, Prisma.TripUpdateWithoutImagesInput>, Prisma.TripUncheckedUpdateWithoutImagesInput>
|
||||
}
|
||||
|
||||
export type TripCreateNestedOneWithoutParticipantsInput = {
|
||||
create?: Prisma.XOR<Prisma.TripCreateWithoutParticipantsInput, Prisma.TripUncheckedCreateWithoutParticipantsInput>
|
||||
connectOrCreate?: Prisma.TripCreateOrConnectWithoutParticipantsInput
|
||||
@@ -632,11 +631,11 @@ export type TripCreateWithoutOrganizerInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
|
||||
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripUncheckedCreateWithoutOrganizerInput = {
|
||||
@@ -648,11 +647,11 @@ export type TripUncheckedCreateWithoutOrganizerInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
|
||||
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripCreateOrConnectWithoutOrganizerInput = {
|
||||
@@ -693,13 +692,92 @@ export type TripScalarWhereInput = {
|
||||
date?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
maxParticipants?: Prisma.IntFilter<"Trip"> | number
|
||||
price?: Prisma.IntFilter<"Trip"> | number
|
||||
image?: Prisma.StringNullableFilter<"Trip"> | string | null
|
||||
status?: Prisma.EnumTripStatusFilter<"Trip"> | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
updatedAt?: Prisma.DateTimeFilter<"Trip"> | Date | string
|
||||
organizerId?: Prisma.StringFilter<"Trip"> | string
|
||||
}
|
||||
|
||||
export type TripCreateWithoutImagesInput = {
|
||||
id?: string
|
||||
title: string
|
||||
description?: string | null
|
||||
mountain: string
|
||||
location: string
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
|
||||
participants?: Prisma.TripParticipantCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripUncheckedCreateWithoutImagesInput = {
|
||||
id?: string
|
||||
title: string
|
||||
description?: string | null
|
||||
mountain: string
|
||||
location: string
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
organizerId: string
|
||||
participants?: Prisma.TripParticipantUncheckedCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripCreateOrConnectWithoutImagesInput = {
|
||||
where: Prisma.TripWhereUniqueInput
|
||||
create: Prisma.XOR<Prisma.TripCreateWithoutImagesInput, Prisma.TripUncheckedCreateWithoutImagesInput>
|
||||
}
|
||||
|
||||
export type TripUpsertWithoutImagesInput = {
|
||||
update: Prisma.XOR<Prisma.TripUpdateWithoutImagesInput, Prisma.TripUncheckedUpdateWithoutImagesInput>
|
||||
create: Prisma.XOR<Prisma.TripCreateWithoutImagesInput, Prisma.TripUncheckedCreateWithoutImagesInput>
|
||||
where?: Prisma.TripWhereInput
|
||||
}
|
||||
|
||||
export type TripUpdateToOneWithWhereWithoutImagesInput = {
|
||||
where?: Prisma.TripWhereInput
|
||||
data: Prisma.XOR<Prisma.TripUpdateWithoutImagesInput, Prisma.TripUncheckedUpdateWithoutImagesInput>
|
||||
}
|
||||
|
||||
export type TripUpdateWithoutImagesInput = {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
export type TripUncheckedUpdateWithoutImagesInput = {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
export type TripCreateWithoutParticipantsInput = {
|
||||
id?: string
|
||||
title: string
|
||||
@@ -709,11 +787,11 @@ export type TripCreateWithoutParticipantsInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
organizer: Prisma.UserCreateNestedOneWithoutTripsInput
|
||||
images?: Prisma.TripImageCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripUncheckedCreateWithoutParticipantsInput = {
|
||||
@@ -725,11 +803,11 @@ export type TripUncheckedCreateWithoutParticipantsInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
organizerId: string
|
||||
images?: Prisma.TripImageUncheckedCreateNestedManyWithoutTripInput
|
||||
}
|
||||
|
||||
export type TripCreateOrConnectWithoutParticipantsInput = {
|
||||
@@ -757,11 +835,11 @@ export type TripUpdateWithoutParticipantsInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
organizer?: Prisma.UserUpdateOneRequiredWithoutTripsNestedInput
|
||||
images?: Prisma.TripImageUpdateManyWithoutTripNestedInput
|
||||
}
|
||||
|
||||
export type TripUncheckedUpdateWithoutParticipantsInput = {
|
||||
@@ -773,11 +851,11 @@ export type TripUncheckedUpdateWithoutParticipantsInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
organizerId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||
images?: Prisma.TripImageUncheckedUpdateManyWithoutTripNestedInput
|
||||
}
|
||||
|
||||
export type TripCreateManyOrganizerInput = {
|
||||
@@ -789,7 +867,6 @@ export type TripCreateManyOrganizerInput = {
|
||||
date: Date | string
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image?: string | null
|
||||
status?: $Enums.TripStatus
|
||||
createdAt?: Date | string
|
||||
updatedAt?: Date | string
|
||||
@@ -804,11 +881,11 @@ export type TripUpdateWithoutOrganizerInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
participants?: Prisma.TripParticipantUpdateManyWithoutTripNestedInput
|
||||
images?: Prisma.TripImageUpdateManyWithoutTripNestedInput
|
||||
}
|
||||
|
||||
export type TripUncheckedUpdateWithoutOrganizerInput = {
|
||||
@@ -820,11 +897,11 @@ export type TripUncheckedUpdateWithoutOrganizerInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
participants?: Prisma.TripParticipantUncheckedUpdateManyWithoutTripNestedInput
|
||||
images?: Prisma.TripImageUncheckedUpdateManyWithoutTripNestedInput
|
||||
}
|
||||
|
||||
export type TripUncheckedUpdateManyWithoutOrganizerInput = {
|
||||
@@ -836,7 +913,6 @@ export type TripUncheckedUpdateManyWithoutOrganizerInput = {
|
||||
date?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
maxParticipants?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
price?: Prisma.IntFieldUpdateOperationsInput | number
|
||||
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||
status?: Prisma.EnumTripStatusFieldUpdateOperationsInput | $Enums.TripStatus
|
||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||
@@ -849,10 +925,12 @@ export type TripUncheckedUpdateManyWithoutOrganizerInput = {
|
||||
|
||||
export type TripCountOutputType = {
|
||||
participants: number
|
||||
images: number
|
||||
}
|
||||
|
||||
export type TripCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
participants?: boolean | TripCountOutputTypeCountParticipantsArgs
|
||||
images?: boolean | TripCountOutputTypeCountImagesArgs
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -872,6 +950,13 @@ export type TripCountOutputTypeCountParticipantsArgs<ExtArgs extends runtime.Typ
|
||||
where?: Prisma.TripParticipantWhereInput
|
||||
}
|
||||
|
||||
/**
|
||||
* TripCountOutputType without action
|
||||
*/
|
||||
export type TripCountOutputTypeCountImagesArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
where?: Prisma.TripImageWhereInput
|
||||
}
|
||||
|
||||
|
||||
export type TripSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||
id?: boolean
|
||||
@@ -882,13 +967,13 @@ export type TripSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
||||
date?: boolean
|
||||
maxParticipants?: boolean
|
||||
price?: boolean
|
||||
image?: boolean
|
||||
status?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
organizerId?: boolean
|
||||
organizer?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
participants?: boolean | Prisma.Trip$participantsArgs<ExtArgs>
|
||||
images?: boolean | Prisma.Trip$imagesArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.TripCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}, ExtArgs["result"]["trip"]>
|
||||
|
||||
@@ -901,7 +986,6 @@ export type TripSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensio
|
||||
date?: boolean
|
||||
maxParticipants?: boolean
|
||||
price?: boolean
|
||||
image?: boolean
|
||||
status?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
@@ -918,7 +1002,6 @@ export type TripSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensio
|
||||
date?: boolean
|
||||
maxParticipants?: boolean
|
||||
price?: boolean
|
||||
image?: boolean
|
||||
status?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
@@ -935,17 +1018,17 @@ export type TripSelectScalar = {
|
||||
date?: boolean
|
||||
maxParticipants?: boolean
|
||||
price?: boolean
|
||||
image?: boolean
|
||||
status?: boolean
|
||||
createdAt?: boolean
|
||||
updatedAt?: boolean
|
||||
organizerId?: boolean
|
||||
}
|
||||
|
||||
export type TripOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "title" | "description" | "mountain" | "location" | "date" | "maxParticipants" | "price" | "image" | "status" | "createdAt" | "updatedAt" | "organizerId", ExtArgs["result"]["trip"]>
|
||||
export type TripOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "title" | "description" | "mountain" | "location" | "date" | "maxParticipants" | "price" | "status" | "createdAt" | "updatedAt" | "organizerId", ExtArgs["result"]["trip"]>
|
||||
export type TripInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
organizer?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||
participants?: boolean | Prisma.Trip$participantsArgs<ExtArgs>
|
||||
images?: boolean | Prisma.Trip$imagesArgs<ExtArgs>
|
||||
_count?: boolean | Prisma.TripCountOutputTypeDefaultArgs<ExtArgs>
|
||||
}
|
||||
export type TripIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
@@ -960,6 +1043,7 @@ export type $TripPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
objects: {
|
||||
organizer: Prisma.$UserPayload<ExtArgs>
|
||||
participants: Prisma.$TripParticipantPayload<ExtArgs>[]
|
||||
images: Prisma.$TripImagePayload<ExtArgs>[]
|
||||
}
|
||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||
id: string
|
||||
@@ -970,7 +1054,6 @@ export type $TripPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
||||
date: Date
|
||||
maxParticipants: number
|
||||
price: number
|
||||
image: string | null
|
||||
status: $Enums.TripStatus
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
@@ -1371,6 +1454,7 @@ export interface Prisma__TripClient<T, Null = never, ExtArgs extends runtime.Typ
|
||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||
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>
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
@@ -1408,7 +1492,6 @@ export interface TripFieldRefs {
|
||||
readonly date: Prisma.FieldRef<"Trip", 'DateTime'>
|
||||
readonly maxParticipants: Prisma.FieldRef<"Trip", 'Int'>
|
||||
readonly price: Prisma.FieldRef<"Trip", 'Int'>
|
||||
readonly image: Prisma.FieldRef<"Trip", 'String'>
|
||||
readonly status: Prisma.FieldRef<"Trip", 'TripStatus'>
|
||||
readonly createdAt: Prisma.FieldRef<"Trip", 'DateTime'>
|
||||
readonly updatedAt: Prisma.FieldRef<"Trip", 'DateTime'>
|
||||
@@ -1837,6 +1920,30 @@ export type Trip$participantsArgs<ExtArgs extends runtime.Types.Extensions.Inter
|
||||
distinct?: Prisma.TripParticipantScalarFieldEnum | Prisma.TripParticipantScalarFieldEnum[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Trip.images
|
||||
*/
|
||||
export type Trip$imagesArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||
/**
|
||||
* Select specific fields to fetch from the TripImage
|
||||
*/
|
||||
select?: Prisma.TripImageSelect<ExtArgs> | null
|
||||
/**
|
||||
* Omit specific fields from the TripImage
|
||||
*/
|
||||
omit?: Prisma.TripImageOmit<ExtArgs> | null
|
||||
/**
|
||||
* Choose, which related nodes to fetch as well
|
||||
*/
|
||||
include?: Prisma.TripImageInclude<ExtArgs> | null
|
||||
where?: Prisma.TripImageWhereInput
|
||||
orderBy?: Prisma.TripImageOrderByWithRelationInput | Prisma.TripImageOrderByWithRelationInput[]
|
||||
cursor?: Prisma.TripImageWhereUniqueInput
|
||||
take?: number
|
||||
skip?: number
|
||||
distinct?: Prisma.TripImageScalarFieldEnum | Prisma.TripImageScalarFieldEnum[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Trip without action
|
||||
*/
|
||||
|
||||
@@ -41,10 +41,6 @@
|
||||
--color-neutral-900: #111827;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f9fafb;
|
||||
color: #1f2937;
|
||||
|
||||
@@ -28,6 +28,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html
|
||||
lang="id"
|
||||
data-scroll-behavior="smooth"
|
||||
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
||||
>
|
||||
<body className="flex min-h-full flex-col bg-neutral-50">
|
||||
|
||||
@@ -85,7 +85,7 @@ export default async function HomePage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{upcomingTrips.slice(0, 3).map((trip) => (
|
||||
{upcomingTrips.slice(0, 3).map((trip, i) => (
|
||||
<TripCard
|
||||
key={trip.id}
|
||||
id={trip.id}
|
||||
@@ -98,6 +98,8 @@ export default async function HomePage() {
|
||||
participantCount={trip._count.participants}
|
||||
organizerName={trip.organizer.name}
|
||||
status={trip.status}
|
||||
coverImage={trip.images[0]?.url}
|
||||
priority={i === 0}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -161,6 +163,7 @@ export default async function HomePage() {
|
||||
participantCount={trip._count.participants}
|
||||
organizerName={trip.organizer.name}
|
||||
status={trip.status}
|
||||
coverImage={trip.images[0]?.url}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -197,6 +200,7 @@ export default async function HomePage() {
|
||||
participantCount={trip._count.participants}
|
||||
organizerName={trip.organizer.name}
|
||||
status={trip.status}
|
||||
coverImage={trip.images[0]?.url}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { authOptions } from "@/lib/auth";
|
||||
import { tripService } from "@/server/services/trip.service";
|
||||
import { formatRupiah, formatDate } from "@/lib/utils";
|
||||
import { JoinTripButton } from "@/features/trip/components/join-trip-button";
|
||||
import { ImageGallery } from "@/features/trip/components/image-gallery";
|
||||
|
||||
export default async function TripDetailPage({
|
||||
params,
|
||||
@@ -49,33 +50,35 @@ export default async function TripDetailPage({
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-2xl border border-neutral-200 bg-white shadow-sm">
|
||||
{/* Header — dark theme */}
|
||||
<div className="relative overflow-hidden bg-neutral-800 px-6 py-8">
|
||||
<div className="absolute inset-0 bg-linear-to-br from-primary-900/40 to-secondary-900/30" />
|
||||
<div className="relative">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white">{trip.title}</h1>
|
||||
<p className="mt-1 flex items-center gap-1.5 text-neutral-300">
|
||||
<span>🏔️</span> {trip.mountain}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className={`rounded-full px-3 py-1 text-xs font-bold ${
|
||||
trip.status === "OPEN"
|
||||
? "bg-primary-500/20 text-primary-300 ring-1 ring-primary-400/30"
|
||||
: trip.status === "FULL"
|
||||
? "bg-amber-500/20 text-amber-300 ring-1 ring-amber-400/30"
|
||||
: "bg-neutral-500/20 text-neutral-400 ring-1 ring-neutral-500/30"
|
||||
}`}
|
||||
>
|
||||
{trip.status}
|
||||
</span>
|
||||
{/* Image Gallery */}
|
||||
<ImageGallery images={trip.images} />
|
||||
|
||||
{/* Title bar */}
|
||||
<div className="border-b border-neutral-100 px-6 py-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-neutral-800">
|
||||
{trip.title}
|
||||
</h1>
|
||||
<p className="mt-0.5 flex items-center gap-1.5 text-sm text-neutral-500">
|
||||
🏔️ {trip.mountain}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className={`rounded-full px-3 py-1 text-xs font-bold ${
|
||||
trip.status === "OPEN"
|
||||
? "bg-primary-100 text-primary-700"
|
||||
: trip.status === "FULL"
|
||||
? "bg-amber-100 text-amber-700"
|
||||
: "bg-neutral-100 text-neutral-500"
|
||||
}`}
|
||||
>
|
||||
{trip.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="space-y-6 p-6">
|
||||
{/* Info Grid */}
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className="flex items-center gap-3 rounded-xl bg-neutral-50 p-4">
|
||||
|
||||
@@ -57,6 +57,7 @@ export default async function TripsPage() {
|
||||
participantCount={trip._count.participants}
|
||||
organizerName={trip.organizer.name}
|
||||
status={trip.status}
|
||||
coverImage={trip.images[0]?.url}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,6 @@ export async function createTripAction(formData: FormData) {
|
||||
date: formData.get("date") as string,
|
||||
maxParticipants: formData.get("maxParticipants") as string,
|
||||
price: formData.get("price") as string,
|
||||
image: formData.get("image") as string,
|
||||
};
|
||||
|
||||
const result = createTripSchema.safeParse(raw);
|
||||
@@ -28,11 +27,18 @@ export async function createTripAction(formData: FormData) {
|
||||
return { error: result.error.issues[0].message };
|
||||
}
|
||||
|
||||
// Collect image URLs from form (multiple inputs named "imageUrls")
|
||||
const imageUrls = formData
|
||||
.getAll("imageUrls")
|
||||
.map((v) => (v as string).trim())
|
||||
.filter(Boolean);
|
||||
|
||||
try {
|
||||
const trip = await tripService.createTrip({
|
||||
...result.data,
|
||||
date: new Date(result.data.date),
|
||||
organizerId: session.user.id,
|
||||
imageUrls: imageUrls.length > 0 ? imageUrls : undefined,
|
||||
});
|
||||
revalidatePath("/trips");
|
||||
return { success: true, tripId: trip.id };
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
|
||||
interface TripImage {
|
||||
id: string;
|
||||
url: string;
|
||||
caption: string | null;
|
||||
}
|
||||
|
||||
export function ImageGallery({ images }: { images: TripImage[] }) {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
|
||||
if (images.length === 0) {
|
||||
return (
|
||||
<div className="flex h-56 items-center justify-center bg-linear-to-br from-primary-800 to-secondary-900 sm:h-72">
|
||||
<span className="text-6xl">🏔️</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const activeImage = images[activeIndex];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Main Image */}
|
||||
<div className="relative h-56 bg-neutral-900 sm:h-72">
|
||||
<Image
|
||||
src={activeImage.url}
|
||||
alt={activeImage.caption || "Foto trip"}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 768px) 100vw, 768px"
|
||||
priority
|
||||
/>
|
||||
{activeImage.caption && (
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/60 to-transparent px-4 pb-3 pt-8">
|
||||
<p className="text-sm font-medium text-white">
|
||||
{activeImage.caption}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{/* Counter */}
|
||||
<div className="absolute right-3 top-3 rounded-full bg-black/50 px-2.5 py-1 text-xs font-medium text-white backdrop-blur-sm">
|
||||
{activeIndex + 1} / {images.length}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Thumbnails */}
|
||||
{images.length > 1 && (
|
||||
<div className="flex gap-1.5 overflow-x-auto bg-neutral-100 p-2">
|
||||
{images.map((img, i) => (
|
||||
<button
|
||||
key={img.id}
|
||||
onClick={() => setActiveIndex(i)}
|
||||
className={`relative h-14 w-20 shrink-0 overflow-hidden rounded-lg transition-all ${
|
||||
i === activeIndex
|
||||
? "ring-2 ring-primary-500 ring-offset-1"
|
||||
: "opacity-60 hover:opacity-100"
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={img.url}
|
||||
alt={img.caption || `Foto ${i + 1}`}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="80px"
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export function ImageUrlInput() {
|
||||
const [urls, setUrls] = useState<string[]>([""]);
|
||||
|
||||
function addField() {
|
||||
if (urls.length < 5) {
|
||||
setUrls([...urls, ""]);
|
||||
}
|
||||
}
|
||||
|
||||
function removeField(index: number) {
|
||||
setUrls(urls.filter((_, i) => i !== index));
|
||||
}
|
||||
|
||||
function updateField(index: number, value: string) {
|
||||
const updated = [...urls];
|
||||
updated[index] = value;
|
||||
setUrls(updated);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-semibold text-neutral-700">
|
||||
Foto Trip (URL)
|
||||
</span>
|
||||
<span className="text-xs text-neutral-400">{urls.length}/5</span>
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
{urls.map((url, i) => (
|
||||
<div key={i} className="flex gap-2">
|
||||
<input
|
||||
name="imageUrls"
|
||||
type="url"
|
||||
value={url}
|
||||
onChange={(e) => updateField(i, e.target.value)}
|
||||
className="flex-1 rounded-xl border border-neutral-200 bg-neutral-50 px-4 py-2.5 text-sm text-neutral-800 placeholder:text-neutral-400 focus:bg-white"
|
||||
placeholder={
|
||||
i === 0
|
||||
? "URL foto utama (cover)"
|
||||
: `URL foto ${i + 1} (opsional)`
|
||||
}
|
||||
/>
|
||||
{urls.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeField(i)}
|
||||
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-neutral-200 text-neutral-400 hover:bg-red-50 hover:text-red-500"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{urls.length < 5 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={addField}
|
||||
className="mt-2 flex items-center gap-1 rounded-lg px-2 py-1 text-sm font-medium text-secondary-600 hover:bg-secondary-50"
|
||||
>
|
||||
+ Tambah foto
|
||||
</button>
|
||||
)}
|
||||
<p className="mt-1.5 text-xs text-neutral-400">
|
||||
Upload foto ke hosting (imgur, imgbb, dll) lalu paste URL-nya di sini
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { formatRupiah, formatDate } from "@/lib/utils";
|
||||
|
||||
@@ -12,6 +13,8 @@ interface TripCardProps {
|
||||
participantCount: number;
|
||||
organizerName: string;
|
||||
status: string;
|
||||
coverImage?: string | null;
|
||||
priority?: boolean;
|
||||
}
|
||||
|
||||
export function TripCard({
|
||||
@@ -25,58 +28,76 @@ export function TripCard({
|
||||
participantCount,
|
||||
organizerName,
|
||||
status,
|
||||
coverImage,
|
||||
priority,
|
||||
}: TripCardProps) {
|
||||
const spotsLeft = maxParticipants - participantCount;
|
||||
|
||||
return (
|
||||
<Link href={`/trips/${id}`} className="group block">
|
||||
<div className="rounded-2xl border border-neutral-200 bg-white p-5 transition-all group-hover:-translate-y-0.5 group-hover:shadow-lg group-hover:shadow-neutral-200/60">
|
||||
{/* Header */}
|
||||
<div className="mb-3 flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="font-bold text-neutral-800 group-hover:text-primary-700">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="mt-0.5 text-sm text-neutral-500">{mountain}</p>
|
||||
</div>
|
||||
<div className="overflow-hidden rounded-2xl border border-neutral-200 bg-white transition-all group-hover:-translate-y-0.5 group-hover:shadow-lg group-hover:shadow-neutral-200/60">
|
||||
{/* Cover Image */}
|
||||
<div className="relative h-40 bg-neutral-800">
|
||||
{coverImage ? (
|
||||
<Image
|
||||
src={coverImage}
|
||||
alt={title}
|
||||
fill
|
||||
className="object-cover transition-transform group-hover:scale-105"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
priority={priority}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center bg-linear-to-br from-primary-800 to-secondary-900">
|
||||
<span className="text-4xl">🏔️</span>
|
||||
</div>
|
||||
)}
|
||||
<span
|
||||
className={`rounded-full px-2.5 py-0.5 text-xs font-semibold ${
|
||||
className={`absolute right-3 top-3 rounded-full px-2.5 py-0.5 text-xs font-bold backdrop-blur-sm ${
|
||||
status === "OPEN"
|
||||
? "bg-primary-100 text-primary-700"
|
||||
? "bg-primary-600/80 text-white"
|
||||
: status === "FULL"
|
||||
? "bg-amber-100 text-amber-700"
|
||||
: "bg-neutral-100 text-neutral-500"
|
||||
? "bg-amber-500/80 text-white"
|
||||
: "bg-neutral-600/80 text-neutral-200"
|
||||
}`}
|
||||
>
|
||||
{status}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div className="space-y-1.5 text-sm text-neutral-600">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-secondary-500">📍</span> {location}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-secondary-500">📅</span> {formatDate(date)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-secondary-500">👤</span> {organizerName}
|
||||
</div>
|
||||
</div>
|
||||
{/* Content */}
|
||||
<div className="p-4">
|
||||
<h3 className="font-bold text-neutral-800 group-hover:text-primary-700">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="mt-0.5 text-sm text-neutral-500">{mountain}</p>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-4 flex items-center justify-between border-t border-neutral-100 pt-3">
|
||||
<span className="text-lg font-bold text-primary-600">
|
||||
{formatRupiah(price)}
|
||||
</span>
|
||||
<span
|
||||
className={`text-xs font-semibold ${
|
||||
spotsLeft > 0 ? "text-secondary-600" : "text-amber-600"
|
||||
}`}
|
||||
>
|
||||
{spotsLeft > 0 ? `${spotsLeft} slot tersisa` : "Penuh"}
|
||||
</span>
|
||||
<div className="mt-3 space-y-1 text-sm text-neutral-600">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-xs text-secondary-500">📍</span> {location}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-xs text-secondary-500">📅</span>{" "}
|
||||
{formatDate(date)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-xs text-secondary-500">👤</span>{" "}
|
||||
{organizerName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-between border-t border-neutral-100 pt-3">
|
||||
<span className="text-lg font-bold text-primary-600">
|
||||
{formatRupiah(price)}
|
||||
</span>
|
||||
<span
|
||||
className={`text-xs font-semibold ${
|
||||
spotsLeft > 0 ? "text-secondary-600" : "text-amber-600"
|
||||
}`}
|
||||
>
|
||||
{spotsLeft > 0 ? `${spotsLeft} slot tersisa` : "Penuh"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -8,7 +8,6 @@ export const createTripSchema = z.object({
|
||||
date: z.string().refine((val) => !isNaN(Date.parse(val)), "Tanggal tidak valid"),
|
||||
maxParticipants: z.coerce.number().min(1, "Minimal 1 peserta"),
|
||||
price: z.coerce.number().min(0, "Harga tidak valid"),
|
||||
image: z.string().optional(),
|
||||
});
|
||||
|
||||
export type CreateTripInput = z.infer<typeof createTripSchema>;
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "**",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `image` on the `Trip` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Trip" DROP COLUMN "image";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TripImage" (
|
||||
"id" TEXT NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"caption" TEXT,
|
||||
"order" INTEGER NOT NULL DEFAULT 0,
|
||||
"tripId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "TripImage_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TripImage" ADD CONSTRAINT "TripImage_tripId_fkey" FOREIGN KEY ("tripId") REFERENCES "Trip"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -29,7 +29,6 @@ model Trip {
|
||||
date DateTime
|
||||
maxParticipants Int
|
||||
price Int
|
||||
image String?
|
||||
status TripStatus @default(OPEN)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -38,6 +37,17 @@ model Trip {
|
||||
organizer User @relation(fields: [organizerId], references: [id])
|
||||
|
||||
participants TripParticipant[]
|
||||
images TripImage[]
|
||||
}
|
||||
|
||||
model TripImage {
|
||||
id String @id @default(cuid())
|
||||
url String
|
||||
caption String?
|
||||
order Int @default(0)
|
||||
|
||||
tripId String
|
||||
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model TripParticipant {
|
||||
|
||||
@@ -11,81 +11,93 @@ const prisma = new PrismaClient({ adapter });
|
||||
async function main() {
|
||||
console.log("🌱 Seeding database...\n");
|
||||
|
||||
// Clean existing data (order matters for FK)
|
||||
await prisma.tripParticipant.deleteMany();
|
||||
await prisma.tripImage.deleteMany();
|
||||
await prisma.trip.deleteMany();
|
||||
await prisma.user.deleteMany();
|
||||
|
||||
// ==================== USERS ====================
|
||||
|
||||
const password = await bcrypt.hash("password123", 12);
|
||||
|
||||
// Organizer
|
||||
const organizer1 = await prisma.user.upsert({
|
||||
where: { email: "andi@setrip.id" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Andi Pendaki",
|
||||
email: "andi@setrip.id",
|
||||
const dede = await prisma.user.create({
|
||||
data: {
|
||||
name: "Dede Inoen",
|
||||
email: "dede.inoen@setrip.id",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
const organizer2 = await prisma.user.upsert({
|
||||
where: { email: "rina@setrip.id" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Rina Explorer",
|
||||
email: "rina@setrip.id",
|
||||
const panji = await prisma.user.create({
|
||||
data: {
|
||||
name: "Panji Petualang",
|
||||
email: "panji@setrip.id",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
// User biasa (join trip)
|
||||
const user1 = await prisma.user.upsert({
|
||||
where: { email: "budi@gmail.com" },
|
||||
update: {},
|
||||
create: {
|
||||
const fiersa = await prisma.user.create({
|
||||
data: {
|
||||
name: "Fiersa Besari",
|
||||
email: "fiersa@setrip.id",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
// User biasa (peserta)
|
||||
const budi = await prisma.user.create({
|
||||
data: {
|
||||
name: "Budi Santoso",
|
||||
email: "budi@gmail.com",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
const user2 = await prisma.user.upsert({
|
||||
where: { email: "sari@gmail.com" },
|
||||
update: {},
|
||||
create: {
|
||||
const sari = await prisma.user.create({
|
||||
data: {
|
||||
name: "Sari Dewi",
|
||||
email: "sari@gmail.com",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
const user3 = await prisma.user.upsert({
|
||||
where: { email: "doni@gmail.com" },
|
||||
update: {},
|
||||
create: {
|
||||
const doni = await prisma.user.create({
|
||||
data: {
|
||||
name: "Doni Prasetyo",
|
||||
email: "doni@gmail.com",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
const user4 = await prisma.user.upsert({
|
||||
where: { email: "maya@gmail.com" },
|
||||
update: {},
|
||||
create: {
|
||||
const maya = await prisma.user.create({
|
||||
data: {
|
||||
name: "Maya Putri",
|
||||
email: "maya@gmail.com",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
const raka = await prisma.user.create({
|
||||
data: {
|
||||
name: "Raka Aditya",
|
||||
email: "raka@gmail.com",
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
console.log("✅ Users created");
|
||||
console.log(" Organizer: andi@setrip.id, rina@setrip.id");
|
||||
console.log(" Users: budi@gmail.com, sari@gmail.com, doni@gmail.com, maya@gmail.com");
|
||||
console.log(" Organizer: dede.inoen@setrip.id, panji@setrip.id, fiersa@setrip.id");
|
||||
console.log(" Peserta: budi, sari, doni, maya, raka @gmail.com");
|
||||
console.log(" Password semua: password123\n");
|
||||
|
||||
// ==================== TRIPS ====================
|
||||
// ==================== TRIPS + IMAGES ====================
|
||||
|
||||
const now = new Date();
|
||||
const day = 24 * 60 * 60 * 1000;
|
||||
|
||||
// --- Trip 1: Papandayan (by Dede Inoen) ---
|
||||
const trip1 = await prisma.trip.create({
|
||||
data: {
|
||||
title: "Open Trip Papandayan Weekend",
|
||||
@@ -100,14 +112,22 @@ Itinerary:
|
||||
- Minggu: Sunrise → Turun → Pulang`,
|
||||
mountain: "Gunung Papandayan",
|
||||
location: "Garut, Jawa Barat",
|
||||
date: new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000), // 3 hari lagi
|
||||
date: new Date(now.getTime() + 3 * day),
|
||||
maxParticipants: 10,
|
||||
price: 250000,
|
||||
status: "OPEN",
|
||||
organizerId: organizer1.id,
|
||||
organizerId: dede.id,
|
||||
images: {
|
||||
create: [
|
||||
{ url: "/images/seed/papandayan-1.svg", caption: "Kawah Papandayan", order: 0 },
|
||||
{ url: "/images/seed/papandayan-2.svg", caption: "Track menuju puncak", order: 1 },
|
||||
{ url: "/images/seed/papandayan-3.svg", caption: "Camping ground Pondok Salada", order: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --- Trip 2: Ciremai (by Panji Petualang) ---
|
||||
const trip2 = await prisma.trip.create({
|
||||
data: {
|
||||
title: "Pendakian Ciremai via Apuy",
|
||||
@@ -122,14 +142,22 @@ Itinerary:
|
||||
- Hari 2: Summit attack → Turun → Pulang`,
|
||||
mountain: "Gunung Ciremai",
|
||||
location: "Kuningan, Jawa Barat",
|
||||
date: new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000), // 5 hari lagi
|
||||
date: new Date(now.getTime() + 5 * day),
|
||||
maxParticipants: 8,
|
||||
price: 350000,
|
||||
status: "OPEN",
|
||||
organizerId: organizer1.id,
|
||||
organizerId: panji.id,
|
||||
images: {
|
||||
create: [
|
||||
{ url: "/images/seed/ciremai-1.svg", caption: "Puncak Ciremai 3.078 mdpl", order: 0 },
|
||||
{ url: "/images/seed/ciremai-2.svg", caption: "Jalur pendakian via Apuy", order: 1 },
|
||||
{ url: "/images/seed/ciremai-3.svg", caption: "Sunrise dari puncak", order: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --- Trip 3: Gede-Pangrango (by Fiersa Besari) ---
|
||||
const trip3 = await prisma.trip.create({
|
||||
data: {
|
||||
title: "Sunrise Trip Gede-Pangrango",
|
||||
@@ -142,14 +170,23 @@ Itinerary:
|
||||
Start malam, summit saat sunrise. View epic dijamin!`,
|
||||
mountain: "Gunung Gede",
|
||||
location: "Bogor/Cianjur, Jawa Barat",
|
||||
date: new Date(now.getTime() + 6 * 24 * 60 * 60 * 1000), // 6 hari lagi
|
||||
date: new Date(now.getTime() + 6 * day),
|
||||
maxParticipants: 12,
|
||||
price: 280000,
|
||||
status: "OPEN",
|
||||
organizerId: organizer2.id,
|
||||
organizerId: fiersa.id,
|
||||
images: {
|
||||
create: [
|
||||
{ url: "/images/seed/gede-1.svg", caption: "Puncak Gunung Gede", order: 0 },
|
||||
{ url: "/images/seed/gede-2.svg", caption: "Surya Kencana padang edelweis", order: 1 },
|
||||
{ url: "/images/seed/gede-3.svg", caption: "Blue lake / Danau Biru", order: 2 },
|
||||
{ url: "/images/seed/gede-4.svg", caption: "Night hike track Cibodas", order: 3 },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --- Trip 4: Tangkuban Parahu (by Dede Inoen) ---
|
||||
const trip4 = await prisma.trip.create({
|
||||
data: {
|
||||
title: "Trip Hemat Tangkuban Parahu",
|
||||
@@ -162,14 +199,21 @@ Start malam, summit saat sunrise. View epic dijamin!`,
|
||||
Explore Kawah Ratu, Kawah Domas, foto-foto, terus makan sate maranggi!`,
|
||||
mountain: "Gunung Tangkuban Parahu",
|
||||
location: "Bandung, Jawa Barat",
|
||||
date: new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000), // 2 hari lagi
|
||||
date: new Date(now.getTime() + 2 * day),
|
||||
maxParticipants: 15,
|
||||
price: 120000,
|
||||
status: "OPEN",
|
||||
organizerId: organizer2.id,
|
||||
organizerId: dede.id,
|
||||
images: {
|
||||
create: [
|
||||
{ url: "/images/seed/tangkuban-1.svg", caption: "Kawah Ratu", order: 0 },
|
||||
{ url: "/images/seed/tangkuban-2.svg", caption: "Kawah Domas", order: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --- Trip 5: Malabar (by Fiersa Besari) ---
|
||||
const trip5 = await prisma.trip.create({
|
||||
data: {
|
||||
title: "Malabar Night Hike",
|
||||
@@ -182,14 +226,22 @@ Explore Kawah Ratu, Kawah Domas, foto-foto, terus makan sate maranggi!`,
|
||||
Trip ringan, 3-4 jam naik. Cocok buat yang mau healing malam-malam.`,
|
||||
mountain: "Gunung Malabar",
|
||||
location: "Bandung, Jawa Barat",
|
||||
date: new Date(now.getTime() + 4 * 24 * 60 * 60 * 1000), // 4 hari lagi
|
||||
date: new Date(now.getTime() + 4 * day),
|
||||
maxParticipants: 10,
|
||||
price: 150000,
|
||||
status: "OPEN",
|
||||
organizerId: organizer1.id,
|
||||
organizerId: fiersa.id,
|
||||
images: {
|
||||
create: [
|
||||
{ url: "/images/seed/malabar-1.svg", caption: "Puncak Malabar malam hari", order: 0 },
|
||||
{ url: "/images/seed/malabar-2.svg", caption: "View Bandung dari atas", order: 1 },
|
||||
{ url: "/images/seed/malabar-3.svg", caption: "Track pendakian", order: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --- Trip 6: Guntur (by Panji Petualang) ---
|
||||
const trip6 = await prisma.trip.create({
|
||||
data: {
|
||||
title: "Guntur Challenge Trip",
|
||||
@@ -202,72 +254,62 @@ Trip ringan, 3-4 jam naik. Cocok buat yang mau healing malam-malam.`,
|
||||
Buat yang suka challenge. Pemandangan kawah aktif dari dekat!`,
|
||||
mountain: "Gunung Guntur",
|
||||
location: "Garut, Jawa Barat",
|
||||
date: new Date(now.getTime() + 10 * 24 * 60 * 60 * 1000), // 10 hari lagi
|
||||
date: new Date(now.getTime() + 10 * day),
|
||||
maxParticipants: 8,
|
||||
price: 300000,
|
||||
status: "OPEN",
|
||||
organizerId: organizer2.id,
|
||||
organizerId: panji.id,
|
||||
images: {
|
||||
create: [
|
||||
{ url: "/images/seed/guntur-1.svg", caption: "Kawah aktif Gunung Guntur", order: 0 },
|
||||
{ url: "/images/seed/guntur-2.svg", caption: "Jalur berbatu menuju puncak", order: 1 },
|
||||
{ url: "/images/seed/guntur-3.svg", caption: "View dari puncak", order: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log("✅ 6 Trips created\n");
|
||||
console.log("✅ 6 Trips + images created\n");
|
||||
|
||||
// ==================== PARTICIPANTS ====================
|
||||
|
||||
// Trip 1 (Papandayan) — 3 peserta
|
||||
await prisma.tripParticipant.createMany({
|
||||
data: [
|
||||
{ tripId: trip1.id, userId: user1.id, status: "CONFIRMED" },
|
||||
{ tripId: trip1.id, userId: user2.id, status: "CONFIRMED" },
|
||||
{ tripId: trip1.id, userId: user3.id, status: "CONFIRMED" },
|
||||
// Papandayan — 4 peserta
|
||||
{ tripId: trip1.id, userId: budi.id, status: "CONFIRMED" },
|
||||
{ tripId: trip1.id, userId: sari.id, status: "CONFIRMED" },
|
||||
{ tripId: trip1.id, userId: doni.id, status: "CONFIRMED" },
|
||||
{ tripId: trip1.id, userId: raka.id, status: "CONFIRMED" },
|
||||
|
||||
// Ciremai — 2 peserta
|
||||
{ tripId: trip2.id, userId: budi.id, status: "CONFIRMED" },
|
||||
{ tripId: trip2.id, userId: maya.id, status: "CONFIRMED" },
|
||||
|
||||
// Gede — 5 peserta
|
||||
{ tripId: trip3.id, userId: budi.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: sari.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: doni.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: maya.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: raka.id, status: "CONFIRMED" },
|
||||
|
||||
// Tangkuban Parahu — 5 peserta
|
||||
{ tripId: trip4.id, userId: budi.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: sari.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: doni.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: maya.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: raka.id, status: "CONFIRMED" },
|
||||
|
||||
// Malabar — 2 peserta
|
||||
{ tripId: trip5.id, userId: sari.id, status: "CONFIRMED" },
|
||||
{ tripId: trip5.id, userId: maya.id, status: "CONFIRMED" },
|
||||
|
||||
// Guntur — 0 peserta
|
||||
],
|
||||
});
|
||||
|
||||
// Trip 2 (Ciremai) — 2 peserta
|
||||
await prisma.tripParticipant.createMany({
|
||||
data: [
|
||||
{ tripId: trip2.id, userId: user1.id, status: "CONFIRMED" },
|
||||
{ tripId: trip2.id, userId: user4.id, status: "CONFIRMED" },
|
||||
],
|
||||
});
|
||||
|
||||
// Trip 3 (Gede) — 4 peserta
|
||||
await prisma.tripParticipant.createMany({
|
||||
data: [
|
||||
{ tripId: trip3.id, userId: user1.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: user2.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: user3.id, status: "CONFIRMED" },
|
||||
{ tripId: trip3.id, userId: user4.id, status: "CONFIRMED" },
|
||||
],
|
||||
});
|
||||
|
||||
// Trip 4 (Tangkuban Parahu) — 5 peserta
|
||||
await prisma.tripParticipant.createMany({
|
||||
data: [
|
||||
{ tripId: trip4.id, userId: user1.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: user2.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: user3.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: user4.id, status: "CONFIRMED" },
|
||||
{ tripId: trip4.id, userId: organizer1.id, status: "CONFIRMED" },
|
||||
],
|
||||
});
|
||||
|
||||
// Trip 5 (Malabar) — 1 peserta
|
||||
await prisma.tripParticipant.createMany({
|
||||
data: [
|
||||
{ tripId: trip5.id, userId: user2.id, status: "CONFIRMED" },
|
||||
],
|
||||
});
|
||||
|
||||
// Trip 6 (Guntur) — belum ada peserta
|
||||
|
||||
console.log("✅ Participants joined trips");
|
||||
console.log(" Papandayan: 3/10 peserta");
|
||||
console.log(" Ciremai: 2/8 peserta");
|
||||
console.log(" Gede: 4/12 peserta");
|
||||
console.log(" Tangkuban Parahu: 5/15 peserta");
|
||||
console.log(" Malabar: 1/10 peserta");
|
||||
console.log(" Guntur: 0/8 peserta\n");
|
||||
console.log("✅ Participants joined");
|
||||
console.log(" Papandayan: 4/10 | Ciremai: 2/8 | Gede: 5/12");
|
||||
console.log(" Tangkuban Parahu: 5/15 | Malabar: 2/10 | Guntur: 0/8\n");
|
||||
|
||||
console.log("🎉 Seed complete!");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad4" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#67e8f9;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#06b6d4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad4)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#0891b2" opacity="0.7"/>
|
||||
<polygon points="0,450 250,270 500,400 800,460 800,500 0,500" fill="#0e7490" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Ciremai</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 839 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad5" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#7dd3fc;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0ea5e9;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad5)"/>
|
||||
<polygon points="0,420 180,240 380,360 580,200 800,390 800,500 0,500" fill="#0284c7" opacity="0.7"/>
|
||||
<polygon points="100,440 320,280 520,420 800,460 800,500 0,500" fill="#0369a1" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Ciremai</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 2</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 841 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad6" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#a5f3fc;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#06e3f0;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad6)"/>
|
||||
<polygon points="0,410 210,210 410,340 610,170 800,385 800,500 0,500" fill="#00d9ff" opacity="0.7"/>
|
||||
<polygon points="120,430 340,270 540,410 800,450 800,500 0,500" fill="#06b6d4" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Ciremai</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 3</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 841 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad7" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#93c5fd;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1e40af;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad7)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#1e3a8a" opacity="0.7"/>
|
||||
<polygon points="0,450 250,270 500,400 800,460 800,500 0,500" fill="#0f172a" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Gede</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 836 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad8" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#bfdbfe;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad8)"/>
|
||||
<polygon points="0,420 180,240 380,360 580,200 800,390 800,500 0,500" fill="#1e40af" opacity="0.7"/>
|
||||
<polygon points="100,440 320,280 520,420 800,460 800,500 0,500" fill="#1f2937" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Gede</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 2</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 838 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad9" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#dbeafe;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0c4a6e;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad9)"/>
|
||||
<polygon points="0,410 210,210 410,340 610,170 800,385 800,500 0,500" fill="#082f49" opacity="0.7"/>
|
||||
<polygon points="120,430 340,270 540,410 800,450 800,500 0,500" fill="#051e3e" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Gede</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 3</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 838 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad10" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#cffafe;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#164e63;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad10)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#0f2942" opacity="0.7"/>
|
||||
<polygon points="0,450 250,270 500,400 800,460 800,500 0,500" fill="#051924" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Gede</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 4</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 838 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad16" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#fed7aa;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#ea580c;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad16)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#c2410c" opacity="0.7"/>
|
||||
<polygon points="0,450 250,270 500,400 800,460 800,500 0,500" fill="#7c2d12" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Guntur</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 840 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad17" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ffedd5;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#f97316;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad17)"/>
|
||||
<polygon points="0,420 180,240 380,360 580,200 800,390 800,500 0,500" fill="#d97706" opacity="0.7"/>
|
||||
<polygon points="100,440 320,280 520,420 800,460 800,500 0,500" fill="#92400e" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Guntur</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 2</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 842 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad18" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#fecaca;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#ef4444;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad18)"/>
|
||||
<polygon points="0,410 210,210 410,340 610,170 800,385 800,500 0,500" fill="#dc2626" opacity="0.7"/>
|
||||
<polygon points="120,430 340,270 540,410 800,450 800,500 0,500" fill="#991b1b" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Guntur</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 3</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 842 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad13" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#d8b4fe;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#7c3aed;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad13)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#6d28d9" opacity="0.7"/>
|
||||
<polygon points="0,450 250,270 500,400 800,460 800,500 0,500" fill="#3f0f5c" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Malabar</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 841 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad14" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#e9d5ff;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#a855f7;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad14)"/>
|
||||
<polygon points="0,420 180,240 380,360 580,200 800,390 800,500 0,500" fill="#7e22ce" opacity="0.7"/>
|
||||
<polygon points="100,440 320,280 520,420 800,460 800,500 0,500" fill="#44005c" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Malabar</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 2</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 843 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad15" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f3e8ff;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#c084fc;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad15)"/>
|
||||
<polygon points="0,410 210,210 410,340 610,170 800,385 800,500 0,500" fill="#9333ea" opacity="0.7"/>
|
||||
<polygon points="120,430 340,270 540,410 800,450 800,500 0,500" fill="#5b0f8f" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Malabar</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 3</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 843 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#4ade80;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#22c55e;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad1)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#16a34a" opacity="0.7"/>
|
||||
<polygon points="150,420 350,250 550,400 800,450 800,500 0,500" fill="#15803d" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Papandayan</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 844 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad2" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#86efac;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#4ade80;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad2)"/>
|
||||
<polygon points="0,420 180,240 380,360 580,200 800,390 800,500 0,500" fill="#22c55e" opacity="0.7"/>
|
||||
<polygon points="100,440 320,280 520,420 800,460 800,500 0,500" fill="#16a34a" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Papandayan</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 2</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 844 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad3" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#bbf7d0;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#6ee7b7;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad3)"/>
|
||||
<polygon points="0,410 210,210 410,340 610,170 800,385 800,500 0,500" fill="#2dd4bf" opacity="0.7"/>
|
||||
<polygon points="120,430 340,270 540,410 800,450 800,500 0,500" fill="#14b8a6" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Papandayan</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 3</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 844 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad11" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#bfef45;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#84cc16;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad11)"/>
|
||||
<polygon points="0,400 200,200 400,350 600,180 800,380 800,500 0,500" fill="#65a30d" opacity="0.7"/>
|
||||
<polygon points="0,450 250,270 500,400 800,460 800,500 0,500" fill="#3f6212" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Tangkuban</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 1</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 843 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad12" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#d4fc79;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#a3e635;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="500" fill="url(#grad12)"/>
|
||||
<polygon points="0,420 180,240 380,360 580,200 800,390 800,500 0,500" fill="#79c21f" opacity="0.7"/>
|
||||
<polygon points="100,440 320,280 520,420 800,460 800,500 0,500" fill="#4b5320" opacity="0.5"/>
|
||||
<text x="400" y="100" font-size="48" font-weight="bold" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Tangkuban</text>
|
||||
<text x="400" y="150" font-size="24" text-anchor="middle" fill="white" font-family="Arial, sans-serif">Mountain Vista 2</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 845 B |
@@ -6,6 +6,7 @@ export const tripRepo = {
|
||||
return prisma.trip.findMany({
|
||||
include: {
|
||||
organizer: { select: { id: true, name: true, image: true } },
|
||||
images: { orderBy: { order: "asc" }, take: 1 },
|
||||
_count: { select: { participants: true } },
|
||||
},
|
||||
orderBy: { date: "asc" },
|
||||
@@ -17,6 +18,7 @@ export const tripRepo = {
|
||||
where: { status: "OPEN", date: { gte: new Date() } },
|
||||
include: {
|
||||
organizer: { select: { id: true, name: true, image: true } },
|
||||
images: { orderBy: { order: "asc" }, take: 1 },
|
||||
_count: { select: { participants: true } },
|
||||
},
|
||||
orderBy: { date: "asc" },
|
||||
@@ -28,6 +30,7 @@ export const tripRepo = {
|
||||
where: { id },
|
||||
include: {
|
||||
organizer: { select: { id: true, name: true, email: true, image: true } },
|
||||
images: { orderBy: { order: "asc" } },
|
||||
participants: {
|
||||
include: { user: { select: { id: true, name: true, image: true } } },
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ interface CreateTripInput {
|
||||
date: Date;
|
||||
maxParticipants: number;
|
||||
price: number;
|
||||
image?: string;
|
||||
organizerId: string;
|
||||
imageUrls?: string[];
|
||||
}
|
||||
|
||||
export const tripService = {
|
||||
@@ -31,6 +31,12 @@ export const tripService = {
|
||||
},
|
||||
|
||||
async createTrip(input: CreateTripInput) {
|
||||
const images = input.imageUrls?.length
|
||||
? {
|
||||
create: input.imageUrls.map((url, i) => ({ url, order: i })),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return tripRepo.create({
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
@@ -39,8 +45,8 @@ export const tripService = {
|
||||
date: input.date,
|
||||
maxParticipants: input.maxParticipants,
|
||||
price: input.price,
|
||||
image: input.image,
|
||||
organizer: { connect: { id: input.organizerId } },
|
||||
images,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -71,7 +77,6 @@ export const tripService = {
|
||||
|
||||
const participant = await participantRepo.create(tripId, userId);
|
||||
|
||||
// Auto update status if full after join
|
||||
const newCount = await participantRepo.countByTrip(tripId);
|
||||
if (newCount >= trip.maxParticipants) {
|
||||
await tripRepo.updateStatus(tripId, "FULL");
|
||||
@@ -88,7 +93,6 @@ export const tripService = {
|
||||
|
||||
const result = await participantRepo.cancel(tripId, userId);
|
||||
|
||||
// Re-open trip if was full
|
||||
const trip = await tripRepo.findById(tripId);
|
||||
if (trip && trip.status === "FULL") {
|
||||
const count = await participantRepo.countByTrip(tripId);
|
||||
|
||||