Pull Request branch dev-clone to main #1

Merged
gitea merged 429 commits from dev-clone into main 2024-12-23 09:31:34 +00:00
3 changed files with 152 additions and 127 deletions
Showing only changes of commit 196f327289 - Show all commits

View File

@ -43,6 +43,7 @@ const usersRoute = new Hono<HonoEnv>()
* - withMetadata: boolean
*/
// Get all users with search
.get(
"/",
checkPermission("users.readAll"),
@ -59,40 +60,68 @@ const usersRoute = new Hono<HonoEnv>()
.transform((v) => v?.toLowerCase() === "true"),
page: z.coerce.number().int().min(0).default(0),
limit: z.coerce.number().int().min(1).max(1000).default(10),
q: z.string().default(""),
q: z.string().default(""), // Keyword search
})
),
async (c) => {
const { includeTrashed, page, limit, q } = c.req.valid("query");
// Total count for pagination
const totalCountQuery = includeTrashed
? sql<number>`(SELECT count(*) FROM ${users})`
: sql<number>`(SELECT count(*) FROM ${users} WHERE ${users.deletedAt} IS NULL)`;
// Query to get unique user IDs with pagination (Sub Query)
const userIdsQuery = db
// Query to count total data without duplicates
const totalCountQuery = db
.select({
id: users.id,
count: sql<number>`count(distinct ${users.id})`,
})
.from(users)
.leftJoin(respondents, eq(users.id, respondents.userId))
.leftJoin(rolesToUsers, eq(users.id, rolesToUsers.userId))
.leftJoin(rolesSchema, eq(rolesToUsers.roleId, rolesSchema.id))
.where(
and(
includeTrashed ? undefined : isNull(users.deletedAt),
q
? or(
ilike(users.name, q),
ilike(users.username, q),
ilike(users.email, q),
eq(users.id, q)
ilike(users.name, `%${q}%`), // Search by name
ilike(users.username, `%${q}%`), // Search by username
ilike(users.email, `%${q}%`), // Search by email
ilike(respondents.companyName, `%${q}%`), // Search by companyName (from respondents)
ilike(rolesSchema.name, `%${q}%`) // Search by role name (from rolesSchema)
)
: undefined
)
);
// Get the total count result from the query
const totalCountResult = await totalCountQuery;
const totalCount = totalCountResult[0]?.count || 0;
// Query to get unique user IDs based on pagination (Sub Query)
const userIdsQuery = db
.select({
id: users.id,
})
.from(users)
.leftJoin(respondents, eq(users.id, respondents.userId))
.leftJoin(rolesToUsers, eq(users.id, rolesToUsers.userId))
.leftJoin(rolesSchema, eq(rolesToUsers.roleId, rolesSchema.id))
.where(
and(
includeTrashed ? undefined : isNull(users.deletedAt),
q
? or(
ilike(users.name, `%${q}%`), // Search by name
ilike(users.username, `%${q}%`), // Search by username
ilike(users.email, `%${q}%`),
ilike(respondents.companyName, `%${q}%`),
ilike(rolesSchema.name, `%${q}%`)
)
: undefined
)
)
.groupBy(users.id) // Group by user ID to avoid the effect of duplicate data
.offset(page * limit)
.limit(limit);
// Main query
// Main Query
const result = await db
.select({
id: users.id,
@ -108,18 +137,18 @@ const usersRoute = new Hono<HonoEnv>()
name: rolesSchema.name,
id: rolesSchema.id,
},
fullCount: totalCountQuery,
})
.from(users)
.leftJoin(respondents, eq(users.id, respondents.userId))
.leftJoin(rolesToUsers, eq(users.id, rolesToUsers.userId))
.leftJoin(rolesSchema, eq(rolesToUsers.roleId, rolesSchema.id))
.where(inArray(users.id, userIdsQuery)) // using ID from subquery
.orderBy(users.createdAt); // sort by createdAt
.where(inArray(users.id, userIdsQuery)) // Only take data based on IDs from subquery
.orderBy(users.createdAt);
// Group roles for each user to prevent duplication
const userMap = new Map<string, {
// Group roles for each user to avoid duplication
const userMap = new Map<
string,
{
id: string;
name: string;
email: string | null;
@ -130,7 +159,8 @@ const usersRoute = new Hono<HonoEnv>()
deletedAt?: Date;
company: string | null;
roles: { id: string; name: string }[];
}>();
}
>();
result.forEach((item) => {
if (!userMap.has(item.id)) {
@ -144,27 +174,30 @@ const usersRoute = new Hono<HonoEnv>()
updatedAt: item.updatedAt ?? new Date(),
deletedAt: item.deletedAt ?? undefined,
company: item.company,
roles: item.role ? [{ id: item.role.id, name: item.role.name }] : [],
roles: item.role
? [{ id: item.role.id, name: item.role.name }]
: [],
});
} else {
const existingUser = userMap.get(item.id);
if (item.role) {
existingUser?.roles.push({ id: item.role.id, name: item.role.name });
existingUser?.roles.push({
id: item.role.id,
name: item.role.name,
});
}
}
});
// Return user data without duplication and roles in array form
// Return user data without duplicates, with roles array
const groupedData = Array.from(userMap.values());
return c.json({
data: groupedData.map((d) => ({ ...d, fullCount: undefined })),
data: groupedData,
_metadata: {
currentPage: page,
totalPages: Math.ceil(
(Number(result[0]?.fullCount) ?? 0) / limit
),
totalItems: Number(result[0]?.fullCount) ?? 0,
totalPages: Math.ceil(totalCount / limit),
totalItems: totalCount,
perPage: limit,
},
});
@ -270,7 +303,7 @@ const usersRoute = new Hono<HonoEnv>()
.where(eq(respondents.phoneNumber, userData.phoneNumber));
if (existingUser.length > 0) {
throw notFound({
throw forbidden({
message: "Email or username has been registered",
})
}

View File

@ -216,14 +216,6 @@ export default function UserFormModal() {
disableAll: mutation.isPending,
readonlyAll: formType === "detail",
inputs: [
{
type: "text",
label: "Id Pengguna",
readOnly: true,
variant: "filled",
...form.getInputProps("id"),
hidden: !form.values.id,
},
{
type: "text",
label: "Nama",
@ -293,7 +285,7 @@ export default function UserFormModal() {
onClick={() => navigate({ search: {} })}
disabled={mutation.isPending}
>
Close
Tutup
</Button>
{formType !== "detail" && (
<Button
@ -302,7 +294,7 @@ export default function UserFormModal() {
type="submit"
loading={mutation.isPending}
>
Save
Simpan
</Button>
)}
</Flex>

View File

@ -54,7 +54,7 @@ export default function UsersPage() {
if (roles && roles.length > 0) {
return roles.map(role => role.name).join(", ");
}
return <div>Tidak ada peran yang diberikan</div>;
return <div>-</div>;
},
}),