flykup_model_production 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth.js +14 -0
- package/config.js +1 -0
- package/db_connection.js +23 -0
- package/index.js +127 -0
- package/info.txt +1 -0
- package/models/AdminEmail.model.js +36 -0
- package/models/LiveStreamInteraction.model.js +93 -0
- package/models/ProductInteraction.model.js +42 -0
- package/models/Review.model.js +121 -0
- package/models/SearchAnalytics.js +22 -0
- package/models/ShoppableInteraction.model.js +89 -0
- package/models/Wishlist.model.js +30 -0
- package/models/admin.model.js +27 -0
- package/models/appUpdate.model.js +18 -0
- package/models/assets.model.js +32 -0
- package/models/blockedRegion.models.js +27 -0
- package/models/category.model.js +29 -0
- package/models/chat.model.js +496 -0
- package/models/coHostInvitation.model.js +56 -0
- package/models/follow.model.js +37 -0
- package/models/loginlogs.model.js +25 -0
- package/models/notification.model.js +116 -0
- package/models/order.modal.js +285 -0
- package/models/productListing.model.js +132 -0
- package/models/profileInteractions.model.js +45 -0
- package/models/registerShow.model.js +28 -0
- package/models/seller.model.js +299 -0
- package/models/settings.model.js +18 -0
- package/models/shipper.model.js +128 -0
- package/models/shoppableVideo.model.js +177 -0
- package/models/shoppableVideoComment.model.js +58 -0
- package/models/shoppableVideoLike.model.js +30 -0
- package/models/shoppableVideoSave.model.js +28 -0
- package/models/shows.model.js +315 -0
- package/models/stock.model.js +128 -0
- package/models/ticket.model.js +115 -0
- package/models/user.model.js +229 -0
- package/package.json +18 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
// Clear cached model to ensure latest schema is used
|
|
3
|
+
delete mongoose.connection.models.Notification;
|
|
4
|
+
|
|
5
|
+
const notificationSchema = new mongoose.Schema(
|
|
6
|
+
{
|
|
7
|
+
type: {
|
|
8
|
+
type: String,
|
|
9
|
+
enum: [
|
|
10
|
+
"mention",
|
|
11
|
+
"request",
|
|
12
|
+
"follow",
|
|
13
|
+
"approval",
|
|
14
|
+
"seller_status",
|
|
15
|
+
"order_placed",
|
|
16
|
+
"order_status",
|
|
17
|
+
"order_cancelled",
|
|
18
|
+
"return_requested",
|
|
19
|
+
"return_status",
|
|
20
|
+
],
|
|
21
|
+
required: true,
|
|
22
|
+
},
|
|
23
|
+
message: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
title: {
|
|
28
|
+
type: String,
|
|
29
|
+
required: true,
|
|
30
|
+
default: "New Notification",
|
|
31
|
+
},
|
|
32
|
+
body: {
|
|
33
|
+
type: String,
|
|
34
|
+
required: true,
|
|
35
|
+
},
|
|
36
|
+
url: {
|
|
37
|
+
type: String,
|
|
38
|
+
default: "/notifications",
|
|
39
|
+
},
|
|
40
|
+
icon: {
|
|
41
|
+
type: String,
|
|
42
|
+
default:
|
|
43
|
+
"https://d2jp9e7w3mhbvf.cloudfront.net/products/bd654f06-fe3b-4417-b995-2a31a3b25ae2_Logo-Flykup.png",
|
|
44
|
+
},
|
|
45
|
+
image: {
|
|
46
|
+
type: String,
|
|
47
|
+
default: null,
|
|
48
|
+
},
|
|
49
|
+
cutMessage: {
|
|
50
|
+
type: String,
|
|
51
|
+
},
|
|
52
|
+
fromUser: {
|
|
53
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
54
|
+
ref: "users",
|
|
55
|
+
},
|
|
56
|
+
fromSystem: {
|
|
57
|
+
type: String, // e.g., "Flykup Team"
|
|
58
|
+
default: null,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
toUser: {
|
|
62
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
63
|
+
ref: "users",
|
|
64
|
+
required: true,
|
|
65
|
+
},
|
|
66
|
+
link: {
|
|
67
|
+
type: String,
|
|
68
|
+
default: null,
|
|
69
|
+
},
|
|
70
|
+
file: {
|
|
71
|
+
name: String,
|
|
72
|
+
size: String,
|
|
73
|
+
},
|
|
74
|
+
actions: {
|
|
75
|
+
type: [String], // e.g., ["Accept", "Decline"]
|
|
76
|
+
default: [],
|
|
77
|
+
},
|
|
78
|
+
isRead: {
|
|
79
|
+
type: Boolean,
|
|
80
|
+
default: false,
|
|
81
|
+
},
|
|
82
|
+
senderProfile: {
|
|
83
|
+
userId: {
|
|
84
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
85
|
+
ref: "users",
|
|
86
|
+
},
|
|
87
|
+
userName: {
|
|
88
|
+
type: String,
|
|
89
|
+
},
|
|
90
|
+
profileURL: {
|
|
91
|
+
// This is where the profile URL from populated sender will go
|
|
92
|
+
type: String, // Just a string now
|
|
93
|
+
default: null,
|
|
94
|
+
},
|
|
95
|
+
role: {
|
|
96
|
+
type: String,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
metadata: {
|
|
100
|
+
status: String,
|
|
101
|
+
reason: String,
|
|
102
|
+
orderId: mongoose.Schema.Types.ObjectId,
|
|
103
|
+
requestId: mongoose.Schema.Types.ObjectId,
|
|
104
|
+
customerName: String,
|
|
105
|
+
sellerName: String,
|
|
106
|
+
customerProfileURL: String,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
timestamps: true,
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Define and then export, or directly export the model
|
|
115
|
+
const Notification = mongoose.model("Notification", notificationSchema);
|
|
116
|
+
export default Notification;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// order.modal.js
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
|
|
5
|
+
const orderSchema = new mongoose.Schema(
|
|
6
|
+
{
|
|
7
|
+
orderId: {
|
|
8
|
+
type: String,
|
|
9
|
+
required: true,
|
|
10
|
+
unique: true,
|
|
11
|
+
default: () => `ORD-${nanoid(8)}`
|
|
12
|
+
},
|
|
13
|
+
userId: {
|
|
14
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
15
|
+
ref: 'users',
|
|
16
|
+
required: true,
|
|
17
|
+
index: true
|
|
18
|
+
},
|
|
19
|
+
products: [{
|
|
20
|
+
productId: {
|
|
21
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
22
|
+
ref: 'productlistings',
|
|
23
|
+
required: true
|
|
24
|
+
},
|
|
25
|
+
sellerUserId: {
|
|
26
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
27
|
+
ref: 'users',
|
|
28
|
+
required: true
|
|
29
|
+
},
|
|
30
|
+
quantity: {
|
|
31
|
+
type: Number,
|
|
32
|
+
required: true,
|
|
33
|
+
min: 1
|
|
34
|
+
},
|
|
35
|
+
basePrice: {
|
|
36
|
+
type: Number,
|
|
37
|
+
required: true
|
|
38
|
+
},
|
|
39
|
+
gstRate: {
|
|
40
|
+
type: Number,
|
|
41
|
+
required: true
|
|
42
|
+
},
|
|
43
|
+
gstAmount: {
|
|
44
|
+
type: Number,
|
|
45
|
+
required: true
|
|
46
|
+
},
|
|
47
|
+
gstType: {
|
|
48
|
+
type: String,
|
|
49
|
+
enum: ['CGST+SGST', 'IGST',"NON-GST"],
|
|
50
|
+
required: true
|
|
51
|
+
}
|
|
52
|
+
}],
|
|
53
|
+
totalBaseAmount: {
|
|
54
|
+
type: Number,
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
totalGstAmount: {
|
|
58
|
+
type: Number,
|
|
59
|
+
required: true
|
|
60
|
+
},
|
|
61
|
+
totalAmount: {
|
|
62
|
+
type: Number,
|
|
63
|
+
required: true
|
|
64
|
+
},
|
|
65
|
+
gstSplit: {
|
|
66
|
+
cgst: {
|
|
67
|
+
rate: Number,
|
|
68
|
+
amount: Number
|
|
69
|
+
},
|
|
70
|
+
sgst: {
|
|
71
|
+
rate: Number,
|
|
72
|
+
amount: Number
|
|
73
|
+
},
|
|
74
|
+
igst: {
|
|
75
|
+
rate: Number,
|
|
76
|
+
amount: Number
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
deliveryAddress: {
|
|
80
|
+
name: { type: String, maxLength: 50, trim: true, required: true },
|
|
81
|
+
mobile: { type: String, trim: true, maxLength: 15, required: true },
|
|
82
|
+
alternateMobile: { type: String, trim: true, maxLength: 15, default: null },
|
|
83
|
+
line1: { type: String, trim: true, maxLength: 100, required: true },
|
|
84
|
+
line2: { type: String, trim: true, maxLength: 100, default: "" },
|
|
85
|
+
city: { type: String, trim: true, maxLength: 50, required: true },
|
|
86
|
+
state: { type: String, trim: true, maxLength: 50, required: true },
|
|
87
|
+
pincode: { type: String, maxLength: 6, trim: true, required: true },
|
|
88
|
+
addressType: {
|
|
89
|
+
type: String,
|
|
90
|
+
enum: ['home', 'work', 'other'],
|
|
91
|
+
default: 'home'
|
|
92
|
+
},
|
|
93
|
+
// --- ADD THESE FIELDS INSIDE deliveryAddress ---
|
|
94
|
+
addressModified: {
|
|
95
|
+
type: Boolean,
|
|
96
|
+
default: false
|
|
97
|
+
},
|
|
98
|
+
addressModifiedAt: {
|
|
99
|
+
type: Date,
|
|
100
|
+
default: null
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
pickupAddresses: [{
|
|
104
|
+
sellerUserId: {
|
|
105
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
106
|
+
ref: 'users',
|
|
107
|
+
required: true
|
|
108
|
+
},
|
|
109
|
+
name: String,
|
|
110
|
+
mobile: String,
|
|
111
|
+
addressLine1: {
|
|
112
|
+
type: String,
|
|
113
|
+
trim: true,
|
|
114
|
+
maxLength: 150,
|
|
115
|
+
},
|
|
116
|
+
addressLine2: {
|
|
117
|
+
type: String,
|
|
118
|
+
trim: true,
|
|
119
|
+
maxLength: 150,
|
|
120
|
+
},
|
|
121
|
+
city: {
|
|
122
|
+
type: String,
|
|
123
|
+
trim: true,
|
|
124
|
+
maxLength: 50,
|
|
125
|
+
},
|
|
126
|
+
state: {
|
|
127
|
+
type: String,
|
|
128
|
+
trim: true,
|
|
129
|
+
maxLength: 50,
|
|
130
|
+
},
|
|
131
|
+
pincode: {
|
|
132
|
+
type: String,
|
|
133
|
+
trim: true,
|
|
134
|
+
maxLength: 6,
|
|
135
|
+
},
|
|
136
|
+
}],
|
|
137
|
+
paymentMethod: {
|
|
138
|
+
type: String,
|
|
139
|
+
enum: ['CARD', 'UPI', 'NETBANKING', 'WALLET', 'COD', "PENDING_PAYMENT", "CASHFREE", "Online"],
|
|
140
|
+
required: true
|
|
141
|
+
},
|
|
142
|
+
paymentStatus: {
|
|
143
|
+
type: String,
|
|
144
|
+
enum: ['PENDING', 'PAID', 'FAILED', 'REFUNDED'],
|
|
145
|
+
default: 'PENDING',
|
|
146
|
+
required: true
|
|
147
|
+
},
|
|
148
|
+
orderStatus: {
|
|
149
|
+
type: String,
|
|
150
|
+
enum: [
|
|
151
|
+
'PENDING_PAYMENT',
|
|
152
|
+
'ORDERED',
|
|
153
|
+
'PACKED',
|
|
154
|
+
'SHIPPED',
|
|
155
|
+
'OUT_FOR_DELIVERY',
|
|
156
|
+
'DELIVERED',
|
|
157
|
+
'CANCELLED',
|
|
158
|
+
'RETURN_REQUESTED',
|
|
159
|
+
'RETURN_APPROVED',
|
|
160
|
+
'RETURN_SHIPPED',
|
|
161
|
+
'RETURN_RECEIVED',
|
|
162
|
+
'REFUNDED',
|
|
163
|
+
'REPLACEMENT_SHIPPED',
|
|
164
|
+
'REPLACEMENT_DELIVERED'
|
|
165
|
+
],
|
|
166
|
+
// default: 'ORDERED', // Default should be the initial state
|
|
167
|
+
required: true
|
|
168
|
+
},
|
|
169
|
+
sourceType: {
|
|
170
|
+
type: String,
|
|
171
|
+
enum: ['static', 'shoppable_video', 'livestream', 'auction'],
|
|
172
|
+
required: true
|
|
173
|
+
},
|
|
174
|
+
sourceRefId: String,
|
|
175
|
+
deliveryCharge: {
|
|
176
|
+
type: Number,
|
|
177
|
+
default: 40
|
|
178
|
+
},
|
|
179
|
+
statusTimeline: {
|
|
180
|
+
ordered: Date,
|
|
181
|
+
paid: Date,
|
|
182
|
+
packed: Date,
|
|
183
|
+
shipped: Date,
|
|
184
|
+
outForDelivery: Date,
|
|
185
|
+
delivered: Date,
|
|
186
|
+
cancelled: Date,
|
|
187
|
+
returnRequested: Date,
|
|
188
|
+
returnApproved: Date,
|
|
189
|
+
returnShipped: Date,
|
|
190
|
+
returnReceived: Date,
|
|
191
|
+
refunded: Date,
|
|
192
|
+
replacementShipped: Date,
|
|
193
|
+
replacementDelivered: Date
|
|
194
|
+
},
|
|
195
|
+
courierDetails: {
|
|
196
|
+
carrier: String,
|
|
197
|
+
trackingNumber: String,
|
|
198
|
+
estimatedDelivery: Date
|
|
199
|
+
},
|
|
200
|
+
returnDetails: {
|
|
201
|
+
reason: String,
|
|
202
|
+
type: {
|
|
203
|
+
type: String,
|
|
204
|
+
enum: ['REFUND', 'REPLACE']
|
|
205
|
+
},
|
|
206
|
+
items: [{
|
|
207
|
+
productId: mongoose.Schema.Types.ObjectId,
|
|
208
|
+
quantity: Number
|
|
209
|
+
}]
|
|
210
|
+
},
|
|
211
|
+
refundDetails: {
|
|
212
|
+
amount: Number,
|
|
213
|
+
method: String,
|
|
214
|
+
transactionId: String
|
|
215
|
+
},
|
|
216
|
+
replacementDetails: {
|
|
217
|
+
trackingNumber: String,
|
|
218
|
+
replacementOrderId: mongoose.Schema.Types.ObjectId
|
|
219
|
+
},
|
|
220
|
+
returnRequests: [{
|
|
221
|
+
_id: { type: mongoose.Schema.Types.ObjectId, auto: true },
|
|
222
|
+
orderItemId: { // Reference to the specific item in the order
|
|
223
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
224
|
+
required: true
|
|
225
|
+
},
|
|
226
|
+
reason: String,
|
|
227
|
+
additionalNotes: String,
|
|
228
|
+
images: [String],
|
|
229
|
+
status: {
|
|
230
|
+
type: String,
|
|
231
|
+
enum: ['PENDING',
|
|
232
|
+
'APPROVED',
|
|
233
|
+
'REJECTED',
|
|
234
|
+
'RETURN_SHIPPED',
|
|
235
|
+
'RETURN_RECEIVED',
|
|
236
|
+
'REFUNDED'],
|
|
237
|
+
default: 'PENDING'
|
|
238
|
+
},
|
|
239
|
+
shipmentDetails: {
|
|
240
|
+
carrier: String,
|
|
241
|
+
trackingNumber: String,
|
|
242
|
+
receiptImage: String, // S3 key for uploaded image
|
|
243
|
+
shippedAt: Date
|
|
244
|
+
},
|
|
245
|
+
receivedAt: Date,
|
|
246
|
+
refundProcessedAt: Date,
|
|
247
|
+
sellerResponse: String,
|
|
248
|
+
requestedAt: Date,
|
|
249
|
+
processedAt: Date
|
|
250
|
+
}],
|
|
251
|
+
cancelReason: String,
|
|
252
|
+
cancelSource: {
|
|
253
|
+
type: String,
|
|
254
|
+
enum: ['user', 'seller', 'system'],
|
|
255
|
+
default: 'user'
|
|
256
|
+
},
|
|
257
|
+
payment: {
|
|
258
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
259
|
+
ref: 'orderPayment'
|
|
260
|
+
},
|
|
261
|
+
invoiceKey: {
|
|
262
|
+
type: String,
|
|
263
|
+
default: null
|
|
264
|
+
},
|
|
265
|
+
invoiceStatus: {
|
|
266
|
+
type: String,
|
|
267
|
+
enum: ['pending', 'processing', 'completed', 'failed'],
|
|
268
|
+
default: 'pending'
|
|
269
|
+
},
|
|
270
|
+
invoiceRetries: {
|
|
271
|
+
type: Number,
|
|
272
|
+
default: 0
|
|
273
|
+
},
|
|
274
|
+
invoiceError: String
|
|
275
|
+
},
|
|
276
|
+
{ timestamps: true }
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
const Order = mongoose.model('Order', orderSchema);
|
|
281
|
+
export default Order;
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
// const Order = mongoose.models.Order || mongoose.model('Order', orderSchema);
|
|
285
|
+
// export default Order;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// backend/models/ProductListing.js (or similar path)
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
const { Schema } = mongoose;
|
|
4
|
+
|
|
5
|
+
const ProductListingSchema = new Schema(
|
|
6
|
+
{
|
|
7
|
+
sellerId: { type: Schema.Types.ObjectId, ref: "sellers" },
|
|
8
|
+
stockId: { type: Schema.Types.ObjectId, ref: "stocks" },
|
|
9
|
+
title: String,
|
|
10
|
+
description: String,
|
|
11
|
+
images: [
|
|
12
|
+
{
|
|
13
|
+
key: { type: String, maxLength: 255, default: null },
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
category: String,
|
|
17
|
+
subcategory: String,
|
|
18
|
+
hsnNo: String,
|
|
19
|
+
MRP: {
|
|
20
|
+
type: Number,
|
|
21
|
+
min: 0,
|
|
22
|
+
default: null,
|
|
23
|
+
},
|
|
24
|
+
productPrice: {
|
|
25
|
+
type: Number,
|
|
26
|
+
min: 0,
|
|
27
|
+
default: null,
|
|
28
|
+
},
|
|
29
|
+
startingPrice: {
|
|
30
|
+
type: Number,
|
|
31
|
+
min: 0,
|
|
32
|
+
default: null,
|
|
33
|
+
},
|
|
34
|
+
reservedPrice: {
|
|
35
|
+
type: Number,
|
|
36
|
+
min: 0,
|
|
37
|
+
default: null,
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// --- ADDED/UPDATED FIELDS FROM FRONTEND ---
|
|
41
|
+
brand: { type: String, default: null }, // ADDED
|
|
42
|
+
manufacturer: String,
|
|
43
|
+
manufacturerAddress: String, // ADDED in frontend, already exists here
|
|
44
|
+
countryOfOrigin: String,
|
|
45
|
+
netQuantity: { type: String, default: null }, // ADDED (String to accommodate units like '500g')
|
|
46
|
+
packagingType: { type: String, default: null }, // ADDED
|
|
47
|
+
weight: { // For shipping calculations
|
|
48
|
+
value: { type: Number, default: null },
|
|
49
|
+
unit: { type: String, default: null },
|
|
50
|
+
},
|
|
51
|
+
dimensions: { // For shipping calculations
|
|
52
|
+
length: { type: Number, default: null },
|
|
53
|
+
width: { type: Number, default: null },
|
|
54
|
+
height: { type: Number, default: null },
|
|
55
|
+
},
|
|
56
|
+
expiryDate: { type: Date, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
57
|
+
shelfLife: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
58
|
+
batchNumber: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
59
|
+
gstRate: { type: Number, default: null }, // Changed to allow null if needed
|
|
60
|
+
sellerName: String,
|
|
61
|
+
sellerContact: { type: String, default: null }, // ADDED
|
|
62
|
+
sellerGSTIN: { type: String, default: null }, // ADDED
|
|
63
|
+
returnPolicy: [String],
|
|
64
|
+
warranty: {
|
|
65
|
+
hasWarranty: { type: Boolean, default: false }, // Default added
|
|
66
|
+
duration: { type: String, default: null }, // Default added
|
|
67
|
+
},
|
|
68
|
+
fssaiLicenseNo: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
69
|
+
bisCertification: { type: String, default: null }, // ADDED in frontend, already exists here (changed to allow null)
|
|
70
|
+
importerName: { type: String, default: null }, // Default added
|
|
71
|
+
importerAddress: { type: String, default: null }, // Default added
|
|
72
|
+
importerGSTIN: { type: String, default: null }, // Default added
|
|
73
|
+
eWasteCompliance: { type: Boolean, default: false }, // Default added
|
|
74
|
+
recyclablePackaging: { type: Boolean, default: false }, // Default added
|
|
75
|
+
hazardousMaterials: { type: String, default: null }, // Default added
|
|
76
|
+
allowDropshipping: {
|
|
77
|
+
type: Boolean,
|
|
78
|
+
default: false,
|
|
79
|
+
index: true,
|
|
80
|
+
},
|
|
81
|
+
commissionRate: {
|
|
82
|
+
type: Number,
|
|
83
|
+
min: [0, 'Commission rate cannot be negative.'],
|
|
84
|
+
// max: [100, 'Commission rate cannot exceed 100%.'], // Max was 25, changed to 100 based on frontend
|
|
85
|
+
max: [100, 'Commission rate cannot exceed 100%.'],
|
|
86
|
+
default: null,
|
|
87
|
+
// Removed Mongoose-level required validation dependent on allowDropshipping
|
|
88
|
+
// Let application logic handle this if needed, or adjust validator
|
|
89
|
+
// required: [
|
|
90
|
+
// function () { return this.allowDropshipping === true; },
|
|
91
|
+
// 'Commission rate is required when allowing dropshipping.'
|
|
92
|
+
// ],
|
|
93
|
+
// validate: { ... } // Keep or remove validation as needed
|
|
94
|
+
},
|
|
95
|
+
hasReturn: {
|
|
96
|
+
type: Boolean,
|
|
97
|
+
default: false
|
|
98
|
+
},
|
|
99
|
+
returnDays: {
|
|
100
|
+
type: Number,
|
|
101
|
+
min: 0,
|
|
102
|
+
default: null
|
|
103
|
+
},
|
|
104
|
+
size: {
|
|
105
|
+
type: String,
|
|
106
|
+
default: null
|
|
107
|
+
},
|
|
108
|
+
isActive: {
|
|
109
|
+
type: Boolean,
|
|
110
|
+
default: true,
|
|
111
|
+
},ratingSummary: {
|
|
112
|
+
averageRating: { type: Number, default: 0 },
|
|
113
|
+
totalRatings: { type: Number, default: 0 },
|
|
114
|
+
ratingDistribution: {
|
|
115
|
+
1: { type: Number, default: 0 },
|
|
116
|
+
2: { type: Number, default: 0 },
|
|
117
|
+
3: { type: Number, default: 0 },
|
|
118
|
+
4: { type: Number, default: 0 },
|
|
119
|
+
5: { type: Number, default: 0 }
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
totalReviews: { type: Number, default: 0 }
|
|
123
|
+
},
|
|
124
|
+
{ timestamps: true }
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Optional: Ensure strict mode is not preventing fields if you intended flexibility (default is true)
|
|
128
|
+
// ProductListingSchema.set('strict', false); // Use with caution
|
|
129
|
+
|
|
130
|
+
const ProductListing = mongoose.model("productlistings", ProductListingSchema);
|
|
131
|
+
|
|
132
|
+
export default ProductListing;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const profileInteractionSchema = new mongoose.Schema({
|
|
4
|
+
profile: {
|
|
5
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
6
|
+
ref: 'users',
|
|
7
|
+
required: true,
|
|
8
|
+
index: true
|
|
9
|
+
},
|
|
10
|
+
viewer: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: 'users',
|
|
13
|
+
index: true
|
|
14
|
+
},
|
|
15
|
+
location: {
|
|
16
|
+
city: String,
|
|
17
|
+
region: String,
|
|
18
|
+
country: String
|
|
19
|
+
},
|
|
20
|
+
platform: {
|
|
21
|
+
type: String,
|
|
22
|
+
enum: ['web', 'mobile', 'unknown'],
|
|
23
|
+
default: 'mobile'
|
|
24
|
+
},
|
|
25
|
+
device: {
|
|
26
|
+
type: String,
|
|
27
|
+
enum: ['mobile', 'desktop', 'tablet', 'other']
|
|
28
|
+
},
|
|
29
|
+
browser: String,
|
|
30
|
+
os: String,
|
|
31
|
+
ip: { type: String, required: true },
|
|
32
|
+
viewedAt: { type: Date, default: Date.now }
|
|
33
|
+
}, { timestamps: true });
|
|
34
|
+
|
|
35
|
+
// Ensures a user's view is counted only once per profile
|
|
36
|
+
profileInteractionSchema.index({ profile: 1, viewer: 1 }, {
|
|
37
|
+
unique: true,
|
|
38
|
+
partialFilterExpression: { viewer: { $exists: true } }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
profileInteractionSchema.index({ viewedAt: -1 });
|
|
42
|
+
|
|
43
|
+
const ProfileInteraction = mongoose.model('ProfileInteraction', profileInteractionSchema);
|
|
44
|
+
|
|
45
|
+
export default ProfileInteraction;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
const registerShowSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
showId: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: "shows", // Changed back to "Shows" to match your actual model name
|
|
8
|
+
required: true,
|
|
9
|
+
},
|
|
10
|
+
userId: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: "users", // Make sure this matches your User model name
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
registeredAt: {
|
|
16
|
+
type: Date,
|
|
17
|
+
default: Date.now,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{ timestamps: true }
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Add compound index to prevent duplicate registrations
|
|
24
|
+
registerShowSchema.index({ showId: 1, userId: 1 }, { unique: true });
|
|
25
|
+
|
|
26
|
+
const RegisterShow = mongoose.model("RegisterShow", registerShowSchema);
|
|
27
|
+
export default RegisterShow;
|
|
28
|
+
|