feat: allow admin to assign multiple counties to users

This commit is contained in:
2025-12-10 13:01:24 +05:30
parent dd5ba2dd78
commit e3f09cd4aa
10 changed files with 94 additions and 9 deletions

View File

@@ -16,6 +16,7 @@ export type AuthenticatedUser = {
type: string; type: string;
userId?: string; userId?: string;
orgId?: Array<string>; orgId?: Array<string>;
counties?: Array<string>;
role?: string; role?: string;
tenantId: string; tenantId: string;
claims: Array<Claim>; claims: Array<Claim>;

View File

@@ -36,6 +36,7 @@ export async function getSession(
type: "user", type: "user",
userId: user.id, userId: user.id,
orgId: user.orgId ? user.orgId.map((item) => item.toString()) : [], orgId: user.orgId ? user.orgId.map((item) => item.toString()) : [],
counties: user.counties ? user.counties.map((item) => item.toString()) : [],
role: user.role, role: user.role,
tenantId: user.tenantId, tenantId: user.tenantId,
claims: rules[user.role].claims ?? [], claims: rules[user.role].claims ?? [],

View File

@@ -159,6 +159,14 @@ export async function listNotifications(
}); });
} }
if (user.counties && user.counties.length > 0) {
filterObj.push({
"county.id": {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj, filterObj,
sortObj sortObj

View File

@@ -15,7 +15,12 @@ const paymentSchema = new mongoose.Schema({
unique: true, unique: true,
}, },
permitPid: String, permitPid: String,
county: Object, county: {
id: mongoose.Types.ObjectId,
pid: String,
name: String,
avatar: String,
},
client: { client: {
type: mongoose.Types.ObjectId, type: mongoose.Types.ObjectId,
ref: "organization", ref: "organization",

View File

@@ -41,6 +41,14 @@ export async function listPayments(
}); });
} }
if (user.counties && user.counties.length > 0) {
filterObj.push({
"county.id": {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
const pipeline: Array<any> = [ const pipeline: Array<any> = [
{ $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] } }, { $match: { $and: [{ tenantId: user.tenantId }, ...filterObj] } },
]; ];

View File

@@ -108,6 +108,7 @@ export async function getPermit(permitId: string, user: AuthenticatedUser) {
.populate({ path: "assignedTo", select: "pid name avatar" }) .populate({ path: "assignedTo", select: "pid name avatar" })
.populate({ path: "createdBy", select: "pid name avatar" }); .populate({ path: "createdBy", select: "pid name avatar" });
// Don't return the record if the user doesn't have access to the org
if ( if (
permit && permit &&
user.role == "client" && user.role == "client" &&
@@ -115,6 +116,15 @@ export async function getPermit(permitId: string, user: AuthenticatedUser) {
) )
return null; return null;
// Don't return the record if the user doesn't have access to the org
if (
permit &&
user.counties &&
user.counties.length > 0 &&
!user.counties.includes(permit.county.id.toString())
)
return null;
return permit; return permit;
} }
@@ -135,6 +145,14 @@ export async function listPermits(
}); });
} }
if (user.counties && user.counties.length > 0) {
filterObj.push({
"county.id": {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj, filterObj,
sortObj sortObj
@@ -410,6 +428,14 @@ export async function searchPermit(
}); });
} }
if (user.counties && user.counties.length > 0) {
filterObj.push({
"county.id": {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
if (!params.searchToken) if (!params.searchToken)
return { permits: [], metadata: { count: 0, page, pageSize } }; return { permits: [], metadata: { count: 0, page, pageSize } };

View File

@@ -149,6 +149,14 @@ export async function listProcessedPermits(
}); });
} }
if (user.counties && user.counties.length > 0) {
filterObj.push({
"county.id": {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj, filterObj,
sortObj sortObj

View File

@@ -90,6 +90,14 @@ export async function listRts(
}); });
} }
if (user.counties && user.counties.length > 0) {
filterObj.push({
county: {
$in: user.counties.map((item) => new mongoose.Types.ObjectId(item)),
},
});
}
let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter( let { taggedFilter, taggedUserFilterIndex } = getTaggedUsersFilter(
filterObj, filterObj,
sortObj sortObj

View File

@@ -14,6 +14,7 @@ const userSchema = new mongoose.Schema({
required: true, required: true,
}, },
orgId: { type: [Schema.Types.ObjectId], ref: "organization" }, orgId: { type: [Schema.Types.ObjectId], ref: "organization" },
counties: { type: [Schema.Types.ObjectId], ref: "organization" },
firstName: String, firstName: String,
lastName: String, lastName: String,
name: String, name: String,
@@ -74,6 +75,7 @@ const userCore = {
avatar: z.string().optional(), avatar: z.string().optional(),
role: z.enum(roles), role: z.enum(roles),
orgId: z.array(z.string()).optional(), orgId: z.array(z.string()).optional(),
counties: z.array(z.string()).optional(),
password: z.string().optional(), password: z.string().optional(),
}; };
@@ -99,6 +101,7 @@ const updateUserInput = z.object({
avatar: z.string().url().optional(), avatar: z.string().url().optional(),
role: z.enum(roles).optional(), role: z.enum(roles).optional(),
orgId: z.array(z.string()).optional(), orgId: z.array(z.string()).optional(),
counties: z.array(z.string()).optional(),
}); });
const userResponse = z.object({ const userResponse = z.object({
@@ -110,6 +113,17 @@ const userResponse = z.object({
_id: z.string().optional(), _id: z.string().optional(),
pid: z.string().optional(), pid: z.string().optional(),
name: z.string().optional(), name: z.string().optional(),
avatar: z.string().optional(),
})
)
.optional(),
counties: z
.array(
z.object({
_id: z.string().optional(),
pid: z.string().optional(),
name: z.string().optional(),
avatar: z.string().optional(),
}) })
) )
.optional(), .optional(),

View File

@@ -74,7 +74,8 @@ export async function createUser(
return userModel return userModel
.findOne({ pid: newUser.pid }) .findOne({ pid: newUser.pid })
.populate({ path: "orgId", select: "pid name avatar" }); .populate({ path: "orgId", select: "pid name avatar" })
.populate({ path: "counties", select: "pid name avatar" });
} }
export async function resetUser(userId: string, user?: AuthenticatedUser) { export async function resetUser(userId: string, user?: AuthenticatedUser) {
@@ -120,14 +121,16 @@ export async function getUser(userId: string) {
if (mongoose.Types.ObjectId.isValid(userId)) { if (mongoose.Types.ObjectId.isValid(userId)) {
return await userModel return await userModel
.findById(userId) .findById(userId)
.populate({ path: "orgId", select: "_id pid name" }); .populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "counties", select: "_id pid name avatar" });
} }
return await userModel return await userModel
.findOne({ .findOne({
$and: [{ pid: userId }], $and: [{ pid: userId }],
}) })
.populate({ path: "orgId", select: "_id pid name" }); .populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "counties", select: "_id pid name avatar" });
} }
export async function getUserWithoutPopulate(userId: string) { export async function getUserWithoutPopulate(userId: string) {
@@ -157,17 +160,19 @@ export async function listUsers(user: AuthenticatedUser) {
{ dev: { $ne: true } }, { dev: { $ne: true } },
], ],
}) })
.select("_id pid orgId firstName lastName name email avatar") .select("_id pid orgId firstName lastName name email avatar counties")
.populate({ path: "orgId", select: "_id pid name avatar" }) .populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "counties", select: "_id pid name avatar" })
.populate({ path: "createdBy", select: "_id pid name avatar" }); .populate({ path: "createdBy", select: "_id pid name avatar" });
} }
return await userModel return await userModel
.find({ $and: [{ tenantId: user.tenantId }, { dev: { $ne: true } }] }) .find({ $and: [{ tenantId: user.tenantId }, { dev: { $ne: true } }] })
.select( .select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin" "_id pid orgId counties firstName lastName name email role avatar status createdAt createdBy lastLogin"
) )
.populate({ path: "orgId", select: "_id pid name avatar" }) .populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "counties", select: "_id pid name avatar" })
.populate({ path: "createdBy", select: "_id pid name avatar" }); .populate({ path: "createdBy", select: "_id pid name avatar" });
} }
@@ -188,9 +193,10 @@ export async function updateUser(
new: true, new: true,
}) })
.select( .select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin" "_id pid orgId counties firstName lastName name email role avatar status createdAt createdBy lastLogin"
) )
.populate({ path: "orgId", select: "_id pid name avatar" }); .populate({ path: "orgId", select: "_id pid name avatar" })
.populate({ path: "counties", select: "_id pid name avatar" });
if (!userInDb) return null; if (!userInDb) return null;
@@ -212,7 +218,7 @@ export async function updateUserInternal(
new: true, new: true,
}) })
.select( .select(
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin" "_id pid orgId counties firstName lastName name email role avatar status createdAt createdBy lastLogin"
); );
} }